From 81310f26eb81e7f36c99ff5b0b46e4ef5874e0dc Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Fri, 3 Feb 2023 19:22:13 -0600 Subject: [PATCH 01/65] merge: aufs-kbuild Signed-off-by: Robert Nelson --- fs/Kconfig | 1 + fs/Makefile | 1 + 2 files changed, 2 insertions(+) diff --git a/fs/Kconfig b/fs/Kconfig index da524c4d7b7e0..50ab89368c2b5 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -288,6 +288,7 @@ source "fs/sysv/Kconfig" source "fs/ufs/Kconfig" source "fs/erofs/Kconfig" source "fs/vboxsf/Kconfig" +source "fs/aufs/Kconfig" endif # MISC_FILESYSTEMS diff --git a/fs/Makefile b/fs/Makefile index c660ce28f1498..6a1a2b40a653d 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -134,3 +134,4 @@ obj-$(CONFIG_EFIVAR_FS) += efivarfs/ obj-$(CONFIG_EROFS_FS) += erofs/ obj-$(CONFIG_VBOXSF_FS) += vboxsf/ obj-$(CONFIG_ZONEFS_FS) += zonefs/ +obj-$(CONFIG_AUFS_FS) += aufs/ From 62d967eb374b854d55e37ffa6b02c0894bfc7fd1 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Fri, 3 Feb 2023 19:22:13 -0600 Subject: [PATCH 02/65] merge: aufs-base Signed-off-by: Robert Nelson --- MAINTAINERS | 13 +++++++++++++ drivers/block/loop.c | 18 ++++++++++++++++++ fs/dcache.c | 2 +- fs/fcntl.c | 4 +++- fs/namespace.c | 6 ++++++ fs/splice.c | 10 +++++----- include/linux/fs.h | 4 ++++ include/linux/lockdep.h | 3 +++ include/linux/mnt_namespace.h | 3 +++ include/linux/splice.h | 6 ++++++ kernel/locking/lockdep.c | 3 ++- 11 files changed, 64 insertions(+), 8 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index c3d78f54af4cb..6421e02429e57 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3007,6 +3007,19 @@ F: include/linux/audit.h F: include/uapi/linux/audit.h F: kernel/audit* +AUFS (advanced multi layered unification filesystem) FILESYSTEM +M: "J. R. Okajima" +L: aufs-users@lists.sourceforge.net (members only) +L: linux-unionfs@vger.kernel.org +S: Supported +W: http://aufs.sourceforge.net +T: git://github.com/sfjro/aufs4-linux.git +F: Documentation/ABI/testing/debugfs-aufs +F: Documentation/ABI/testing/sysfs-aufs +F: Documentation/filesystems/aufs/ +F: fs/aufs/ +F: include/uapi/linux/aufs_type.h + AUXILIARY DISPLAY DRIVERS M: Miguel Ojeda Sandonis S: Maintained diff --git a/drivers/block/loop.c b/drivers/block/loop.c index b10410585a746..c14f1fca3b1a3 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -752,6 +752,24 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev, return error; } +/* + * for AUFS + * no get/put for file. + */ +struct file *loop_backing_file(struct super_block *sb) +{ + struct file *ret; + struct loop_device *l; + + ret = NULL; + if (MAJOR(sb->s_dev) == LOOP_MAJOR) { + l = sb->s_bdev->bd_disk->private_data; + ret = l->lo_backing_file; + } + return ret; +} +EXPORT_SYMBOL_GPL(loop_backing_file); + /* loop sysfs attributes */ static ssize_t loop_attr_show(struct device *dev, char *page, diff --git a/fs/dcache.c b/fs/dcache.c index ea0485861d937..ddca6240e0db4 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1285,7 +1285,7 @@ enum d_walk_ret { * * The @enter() callbacks are called with d_lock held. */ -static void d_walk(struct dentry *parent, void *data, +void d_walk(struct dentry *parent, void *data, enum d_walk_ret (*enter)(void *, struct dentry *)) { struct dentry *this_parent; diff --git a/fs/fcntl.c b/fs/fcntl.c index fcf34f83bf6a8..9a582f35919a4 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -32,7 +32,7 @@ #define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT | O_NOATIME) -static int setfl(int fd, struct file * filp, unsigned long arg) +int setfl(int fd, struct file *filp, unsigned long arg) { struct inode * inode = file_inode(filp); int error = 0; @@ -63,6 +63,8 @@ static int setfl(int fd, struct file * filp, unsigned long arg) if (filp->f_op->check_flags) error = filp->f_op->check_flags(arg); + if (!error && filp->f_op->setfl) + error = filp->f_op->setfl(filp, arg); if (error) return error; diff --git a/fs/namespace.c b/fs/namespace.c index 046b084136c51..a256f0f9c6c09 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -792,6 +792,12 @@ static inline int check_mnt(struct mount *mnt) return mnt->mnt_ns == current->nsproxy->mnt_ns; } +/* for aufs, CONFIG_AUFS_BR_FUSE */ +int is_current_mnt_ns(struct vfsmount *mnt) +{ + return check_mnt(real_mount(mnt)); +} + /* * vfsmount lock must be held for write */ diff --git a/fs/splice.c b/fs/splice.c index 866d5c2367b23..55b5356262085 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -756,8 +756,8 @@ static int warn_unsupported(struct file *file, const char *op) /* * Attempt to initiate a splice from pipe to file. */ -static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, - loff_t *ppos, size_t len, unsigned int flags) +long do_splice_from(struct pipe_inode_info *pipe, struct file *out, + loff_t *ppos, size_t len, unsigned int flags) { if (unlikely(!out->f_op->splice_write)) return warn_unsupported(out, "write"); @@ -767,9 +767,9 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, /* * Attempt to initiate a splice from a file to a pipe. */ -static long do_splice_to(struct file *in, loff_t *ppos, - struct pipe_inode_info *pipe, size_t len, - unsigned int flags) +long do_splice_to(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags) { int ret; diff --git a/include/linux/fs.h b/include/linux/fs.h index ebfc0b2b4969e..d8e557b8dc096 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1332,6 +1332,7 @@ extern void fasync_free(struct fasync_struct *); /* can be called from interrupts */ extern void kill_fasync(struct fasync_struct **, int, int); +extern int setfl(int fd, struct file *filp, unsigned long arg); extern void __f_setown(struct file *filp, struct pid *, enum pid_type, int force); extern int f_setown(struct file *filp, unsigned long arg, int force); extern void f_delown(struct file *filp); @@ -1851,6 +1852,7 @@ struct file_operations { ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*check_flags)(int); + int (*setfl)(struct file *, unsigned long); int (*flock) (struct file *, int, struct file_lock *); ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); @@ -2339,6 +2341,7 @@ extern int current_umask(void); extern void ihold(struct inode * inode); extern void iput(struct inode *); extern int generic_update_time(struct inode *, struct timespec64 *, int); +extern int update_time(struct inode *, struct timespec64 *, int); /* /sys/fs */ extern struct kobject *fs_kobj; @@ -2575,6 +2578,7 @@ static inline bool sb_is_blkdev_sb(struct super_block *sb) } void emergency_thaw_all(void); +extern int __sync_filesystem(struct super_block *, int); extern int sync_filesystem(struct super_block *); extern const struct file_operations def_blk_fops; extern const struct file_operations def_chr_fops; diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index 2c2586312b447..02dab569a2a04 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -252,6 +252,8 @@ static inline int lockdep_match_key(struct lockdep_map *lock, return lock->key == key; } +struct lock_class *lockdep_hlock_class(struct held_lock *hlock); + /* * Acquire a lock. * @@ -388,6 +390,7 @@ static inline void lockdep_unregister_key(struct lock_class_key *key) #define lockdep_depth(tsk) (0) +#define lockdep_is_held(lock) (1) #define lockdep_is_held_type(l, r) (1) #define lockdep_assert_held(l) do { (void)(l); } while (0) diff --git a/include/linux/mnt_namespace.h b/include/linux/mnt_namespace.h index 8f882f5881e87..6b9808f098435 100644 --- a/include/linux/mnt_namespace.h +++ b/include/linux/mnt_namespace.h @@ -7,12 +7,15 @@ struct mnt_namespace; struct fs_struct; struct user_namespace; struct ns_common; +struct vfsmount; extern struct mnt_namespace *copy_mnt_ns(unsigned long, struct mnt_namespace *, struct user_namespace *, struct fs_struct *); extern void put_mnt_ns(struct mnt_namespace *ns); extern struct ns_common *from_mnt_ns(struct mnt_namespace *); +extern int is_current_mnt_ns(struct vfsmount *mnt); + extern const struct file_operations proc_mounts_operations; extern const struct file_operations proc_mountinfo_operations; extern const struct file_operations proc_mountstats_operations; diff --git a/include/linux/splice.h b/include/linux/splice.h index a55179fd60fc3..8e21c53cf8831 100644 --- a/include/linux/splice.h +++ b/include/linux/splice.h @@ -93,4 +93,10 @@ extern void splice_shrink_spd(struct splice_pipe_desc *); extern const struct pipe_buf_operations page_cache_pipe_buf_ops; extern const struct pipe_buf_operations default_pipe_buf_ops; + +extern long do_splice_from(struct pipe_inode_info *pipe, struct file *out, + loff_t *ppos, size_t len, unsigned int flags); +extern long do_splice_to(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags); #endif diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c index 6cbd2b4444769..52483df4b7f9c 100644 --- a/kernel/locking/lockdep.c +++ b/kernel/locking/lockdep.c @@ -186,7 +186,7 @@ unsigned long max_lock_class_idx; struct lock_class lock_classes[MAX_LOCKDEP_KEYS]; DECLARE_BITMAP(lock_classes_in_use, MAX_LOCKDEP_KEYS); -static inline struct lock_class *hlock_class(struct held_lock *hlock) +inline struct lock_class *lockdep_hlock_class(struct held_lock *hlock) { unsigned int class_idx = hlock->class_idx; @@ -207,6 +207,7 @@ static inline struct lock_class *hlock_class(struct held_lock *hlock) */ return lock_classes + class_idx; } +#define hlock_class(hlock) lockdep_hlock_class(hlock) #ifdef CONFIG_LOCK_STAT static DEFINE_PER_CPU(struct lock_class_stats[MAX_LOCKDEP_KEYS], cpu_lock_stats); From 9712dee190148dbcb2b8c0e3eb3780e620a32b8e Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Fri, 3 Feb 2023 19:22:13 -0600 Subject: [PATCH 03/65] merge: aufs-mmap Signed-off-by: Robert Nelson --- fs/proc/base.c | 2 +- fs/proc/nommu.c | 5 ++- fs/proc/task_mmu.c | 7 +++- fs/proc/task_nommu.c | 5 ++- include/linux/mm.h | 37 +++++++++++++++++ include/linux/mm_types.h | 6 +++ kernel/fork.c | 2 +- mm/Makefile | 1 + mm/filemap.c | 2 +- mm/mmap.c | 39 +++++++++++++++--- mm/nommu.c | 10 ++--- mm/prfile.c | 86 ++++++++++++++++++++++++++++++++++++++++ 12 files changed, 184 insertions(+), 18 deletions(-) create mode 100644 mm/prfile.c diff --git a/fs/proc/base.c b/fs/proc/base.c index 5d52aea8d7e7d..2fbf0ad30eaa9 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -2184,7 +2184,7 @@ static int map_files_get_link(struct dentry *dentry, struct path *path) rc = -ENOENT; vma = find_exact_vma(mm, vm_start, vm_end); if (vma && vma->vm_file) { - *path = vma->vm_file->f_path; + *path = vma_pr_or_file(vma)->f_path; path_get(path); rc = 0; } diff --git a/fs/proc/nommu.c b/fs/proc/nommu.c index 13452b32e2bd5..38acccfef9d49 100644 --- a/fs/proc/nommu.c +++ b/fs/proc/nommu.c @@ -40,7 +40,10 @@ static int nommu_region_show(struct seq_file *m, struct vm_region *region) file = region->vm_file; if (file) { - struct inode *inode = file_inode(region->vm_file); + struct inode *inode; + + file = vmr_pr_or_file(region); + inode = file_inode(file); dev = inode->i_sb->s_dev; ino = inode->i_ino; } diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 8b75a04836b63..042f5c9f7414b 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -280,7 +280,10 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma) const char *name = NULL; if (file) { - struct inode *inode = file_inode(vma->vm_file); + struct inode *inode; + + file = vma_pr_or_file(vma); + inode = file_inode(file); dev = inode->i_sb->s_dev; ino = inode->i_ino; pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT; @@ -1885,7 +1888,7 @@ static int show_numa_map(struct seq_file *m, void *v) struct proc_maps_private *proc_priv = &numa_priv->proc_maps; struct vm_area_struct *vma = v; struct numa_maps *md = &numa_priv->md; - struct file *file = vma->vm_file; + struct file *file = vma_pr_or_file(vma); struct mm_struct *mm = vma->vm_mm; struct mempolicy *pol; char buffer[64]; diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c index a6d21fc0033c6..02c2de31196e0 100644 --- a/fs/proc/task_nommu.c +++ b/fs/proc/task_nommu.c @@ -155,7 +155,10 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma) file = vma->vm_file; if (file) { - struct inode *inode = file_inode(vma->vm_file); + struct inode *inode; + + file = vma_pr_or_file(vma); + inode = file_inode(file); dev = inode->i_sb->s_dev; ino = inode->i_ino; pgoff = (loff_t)vma->vm_pgoff << PAGE_SHIFT; diff --git a/include/linux/mm.h b/include/linux/mm.h index b8b677f47a8da..18c07145ed7e4 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1732,6 +1732,43 @@ static inline void unmap_shared_mapping_range(struct address_space *mapping, unmap_mapping_range(mapping, holebegin, holelen, 0); } +#if IS_ENABLED(CONFIG_AUFS_FS) +extern void vma_do_file_update_time(struct vm_area_struct *, const char[], int); +extern struct file *vma_do_pr_or_file(struct vm_area_struct *, const char[], + int); +extern void vma_do_get_file(struct vm_area_struct *, const char[], int); +extern void vma_do_fput(struct vm_area_struct *, const char[], int); + +#define vma_file_update_time(vma) vma_do_file_update_time(vma, __func__, \ + __LINE__) +#define vma_pr_or_file(vma) vma_do_pr_or_file(vma, __func__, \ + __LINE__) +#define vma_get_file(vma) vma_do_get_file(vma, __func__, __LINE__) +#define vma_fput(vma) vma_do_fput(vma, __func__, __LINE__) + +#ifndef CONFIG_MMU +extern struct file *vmr_do_pr_or_file(struct vm_region *, const char[], int); +extern void vmr_do_fput(struct vm_region *, const char[], int); + +#define vmr_pr_or_file(region) vmr_do_pr_or_file(region, __func__, \ + __LINE__) +#define vmr_fput(region) vmr_do_fput(region, __func__, __LINE__) +#endif /* !CONFIG_MMU */ + +#else + +#define vma_file_update_time(vma) file_update_time((vma)->vm_file) +#define vma_pr_or_file(vma) (vma)->vm_file +#define vma_get_file(vma) get_file((vma)->vm_file) +#define vma_fput(vma) fput((vma)->vm_file) + +#ifndef CONFIG_MMU +#define vmr_pr_or_file(region) (region)->vm_file +#define vmr_fput(region) fput((region)->vm_file) +#endif /* !CONFIG_MMU */ + +#endif /* CONFIG_AUFS_FS */ + extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, unsigned int gup_flags); extern int access_remote_vm(struct mm_struct *mm, unsigned long addr, diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 4eb38918da8f8..ed87c1ba3cf9d 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -282,6 +282,9 @@ struct vm_region { unsigned long vm_top; /* region allocated to here */ unsigned long vm_pgoff; /* the offset in vm_file corresponding to vm_start */ struct file *vm_file; /* the backing file or NULL */ +#if IS_ENABLED(CONFIG_AUFS_FS) + struct file *vm_prfile; /* the virtual backing file or NULL */ +#endif int vm_usage; /* region usage count (access under nommu_region_sem) */ bool vm_icache_flushed : 1; /* true if the icache has been flushed for @@ -361,6 +364,9 @@ struct vm_area_struct { unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZE units */ struct file * vm_file; /* File we map to (can be NULL). */ +#if IS_ENABLED(CONFIG_AUFS_FS) + struct file *vm_prfile; /* shadow of vm_file */ +#endif void * vm_private_data; /* was vm_pte (shared mem) */ #ifdef CONFIG_SWAP diff --git a/kernel/fork.c b/kernel/fork.c index 68efe2a0b4fbc..467b4f45f22dd 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -554,7 +554,7 @@ static __latent_entropy int dup_mmap(struct mm_struct *mm, struct inode *inode = file_inode(file); struct address_space *mapping = file->f_mapping; - get_file(file); + vma_get_file(tmp); if (tmp->vm_flags & VM_DENYWRITE) put_write_access(inode); i_mmap_lock_write(mapping); diff --git a/mm/Makefile b/mm/Makefile index d73aed0fc99c1..bf0989e5043db 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -120,3 +120,4 @@ obj-$(CONFIG_MEMFD_CREATE) += memfd.o obj-$(CONFIG_MAPPING_DIRTY_HELPERS) += mapping_dirty_helpers.o obj-$(CONFIG_PTDUMP_CORE) += ptdump.o obj-$(CONFIG_PAGE_REPORTING) += page_reporting.o +obj-$(CONFIG_AUFS_FS:m=y) += prfile.o diff --git a/mm/filemap.c b/mm/filemap.c index 3a983bc1a71c9..509e00390361c 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -2913,7 +2913,7 @@ vm_fault_t filemap_page_mkwrite(struct vm_fault *vmf) vm_fault_t ret = VM_FAULT_LOCKED; sb_start_pagefault(inode->i_sb); - file_update_time(vmf->vma->vm_file); + vma_file_update_time(vmf->vma); lock_page(page); if (page->mapping != inode->i_mapping) { unlock_page(page); diff --git a/mm/mmap.c b/mm/mmap.c index 33ebda8385b95..5cf7276e95000 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -179,7 +179,7 @@ static struct vm_area_struct *remove_vma(struct vm_area_struct *vma) if (vma->vm_ops && vma->vm_ops->close) vma->vm_ops->close(vma); if (vma->vm_file) - fput(vma->vm_file); + vma_fput(vma); mpol_put(vma_policy(vma)); vm_area_free(vma); return next; @@ -951,7 +951,7 @@ int __vma_adjust(struct vm_area_struct *vma, unsigned long start, if (remove_next) { if (file) { uprobe_munmap(next, next->vm_start, next->vm_end); - fput(file); + vma_fput(vma); } if (next->anon_vma) anon_vma_merge(vma, next); @@ -1904,8 +1904,8 @@ unsigned long mmap_region(struct file *file, unsigned long addr, if (vma->vm_ops && vma->vm_ops->close) vma->vm_ops->close(vma); unmap_and_free_vma: + vma_fput(vma); vma->vm_file = NULL; - fput(file); /* Undo any partial mapping done by a device driver. */ unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end); @@ -2772,7 +2772,7 @@ int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma, goto out_free_mpol; if (new->vm_file) - get_file(new->vm_file); + vma_get_file(new); if (new->vm_ops && new->vm_ops->open) new->vm_ops->open(new); @@ -2791,7 +2791,7 @@ int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma, if (new->vm_ops && new->vm_ops->close) new->vm_ops->close(new); if (new->vm_file) - fput(new->vm_file); + vma_fput(new); unlink_anon_vmas(new); out_free_mpol: mpol_put(vma_policy(new)); @@ -2985,6 +2985,9 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size, unsigned long populate = 0; unsigned long ret = -EINVAL; struct file *file; +#if IS_ENABLED(CONFIG_AUFS_FS) + struct file *prfile; +#endif pr_warn_once("%s (%d) uses deprecated remap_file_pages() syscall. See Documentation/vm/remap_file_pages.rst.\n", current->comm, current->pid); @@ -3059,10 +3062,34 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size, } } +#if IS_ENABLED(CONFIG_AUFS_FS) + vma_get_file(vma); + file = vma->vm_file; + prfile = vma->vm_prfile; + ret = do_mmap(vma->vm_file, start, size, + prot, flags, pgoff, &populate, NULL); + if (!IS_ERR_VALUE(ret) && file && prfile) { + struct vm_area_struct *new_vma; + + new_vma = find_vma(mm, ret); + if (!new_vma->vm_prfile) + new_vma->vm_prfile = prfile; + if (new_vma != vma) + get_file(prfile); + } + /* + * two fput()s instead of vma_fput(vma), + * coz vma may not be available anymore. + */ + fput(file); + if (prfile) + fput(prfile); +#else file = get_file(vma->vm_file); ret = do_mmap(vma->vm_file, start, size, prot, flags, pgoff, &populate, NULL); fput(file); +#endif /* CONFIG_AUFS_FS */ out: mmap_write_unlock(mm); if (populate) @@ -3349,7 +3376,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap, if (anon_vma_clone(new_vma, vma)) goto out_free_mempol; if (new_vma->vm_file) - get_file(new_vma->vm_file); + vma_get_file(new_vma); if (new_vma->vm_ops && new_vma->vm_ops->open) new_vma->vm_ops->open(new_vma); vma_link(mm, new_vma, prev, rb_link, rb_parent); diff --git a/mm/nommu.c b/mm/nommu.c index 0faf39b32cdb9..78ecad7204c86 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -533,7 +533,7 @@ static void __put_nommu_region(struct vm_region *region) up_write(&nommu_region_sem); if (region->vm_file) - fput(region->vm_file); + vmr_fput(region); /* IO memory and memory shared directly out of the pagecache * from ramfs/tmpfs mustn't be released here */ @@ -665,7 +665,7 @@ static void delete_vma(struct mm_struct *mm, struct vm_area_struct *vma) if (vma->vm_ops && vma->vm_ops->close) vma->vm_ops->close(vma); if (vma->vm_file) - fput(vma->vm_file); + vma_fput(vma); put_nommu_region(vma->vm_region); vm_area_free(vma); } @@ -1188,7 +1188,7 @@ unsigned long do_mmap(struct file *file, goto error_just_free; } } - fput(region->vm_file); + vmr_fput(region); kmem_cache_free(vm_region_jar, region); region = pregion; result = start; @@ -1265,10 +1265,10 @@ unsigned long do_mmap(struct file *file, up_write(&nommu_region_sem); error: if (region->vm_file) - fput(region->vm_file); + vmr_fput(region); kmem_cache_free(vm_region_jar, region); if (vma->vm_file) - fput(vma->vm_file); + vma_fput(vma); vm_area_free(vma); return ret; diff --git a/mm/prfile.c b/mm/prfile.c new file mode 100644 index 0000000000000..511543ab1b418 --- /dev/null +++ b/mm/prfile.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Mainly for aufs which mmap(2) different file and wants to print different + * path in /proc/PID/maps. + * Call these functions via macros defined in linux/mm.h. + * + * See Documentation/filesystems/aufs/design/06mmap.txt + * + * Copyright (c) 2014-2021 Junjro R. Okajima + * Copyright (c) 2014 Ian Campbell + */ + +#include +#include +#include + +/* #define PRFILE_TRACE */ +static inline void prfile_trace(struct file *f, struct file *pr, + const char func[], int line, const char func2[]) +{ +#ifdef PRFILE_TRACE + if (pr) + pr_info("%s:%d: %s, %pD2\n", func, line, func2, f); +#endif +} + +void vma_do_file_update_time(struct vm_area_struct *vma, const char func[], + int line) +{ + struct file *f = vma->vm_file, *pr = vma->vm_prfile; + + prfile_trace(f, pr, func, line, __func__); + file_update_time(f); + if (f && pr) + file_update_time(pr); +} + +struct file *vma_do_pr_or_file(struct vm_area_struct *vma, const char func[], + int line) +{ + struct file *f = vma->vm_file, *pr = vma->vm_prfile; + + prfile_trace(f, pr, func, line, __func__); + return (f && pr) ? pr : f; +} + +void vma_do_get_file(struct vm_area_struct *vma, const char func[], int line) +{ + struct file *f = vma->vm_file, *pr = vma->vm_prfile; + + prfile_trace(f, pr, func, line, __func__); + get_file(f); + if (f && pr) + get_file(pr); +} + +void vma_do_fput(struct vm_area_struct *vma, const char func[], int line) +{ + struct file *f = vma->vm_file, *pr = vma->vm_prfile; + + prfile_trace(f, pr, func, line, __func__); + fput(f); + if (f && pr) + fput(pr); +} + +#ifndef CONFIG_MMU +struct file *vmr_do_pr_or_file(struct vm_region *region, const char func[], + int line) +{ + struct file *f = region->vm_file, *pr = region->vm_prfile; + + prfile_trace(f, pr, func, line, __func__); + return (f && pr) ? pr : f; +} + +void vmr_do_fput(struct vm_region *region, const char func[], int line) +{ + struct file *f = region->vm_file, *pr = region->vm_prfile; + + prfile_trace(f, pr, func, line, __func__); + fput(f); + if (f && pr) + fput(pr); +} +#endif /* !CONFIG_MMU */ From 90399632bea73c5cb16bb18c6a0400198600d572 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Fri, 3 Feb 2023 19:22:14 -0600 Subject: [PATCH 04/65] merge: aufs-standalone Signed-off-by: Robert Nelson --- fs/dcache.c | 2 ++ fs/exec.c | 1 + fs/fcntl.c | 1 + fs/file_table.c | 1 + fs/namespace.c | 3 +++ fs/notify/group.c | 1 + fs/open.c | 1 + fs/read_write.c | 2 ++ fs/splice.c | 2 ++ fs/xattr.c | 1 + kernel/locking/lockdep.c | 1 + kernel/task_work.c | 1 + security/security.c | 8 ++++++++ 13 files changed, 25 insertions(+) diff --git a/fs/dcache.c b/fs/dcache.c index ddca6240e0db4..30dec552278dc 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1390,6 +1390,7 @@ void d_walk(struct dentry *parent, void *data, seq = 1; goto again; } +EXPORT_SYMBOL_GPL(d_walk); struct check_mount { struct vfsmount *mnt; @@ -2935,6 +2936,7 @@ void d_exchange(struct dentry *dentry1, struct dentry *dentry2) write_sequnlock(&rename_lock); } +EXPORT_SYMBOL_GPL(d_exchange); /** * d_ancestor - search for an ancestor diff --git a/fs/exec.c b/fs/exec.c index 983295c0b8acf..d1a2467c0532c 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -113,6 +113,7 @@ bool path_noexec(const struct path *path) return (path->mnt->mnt_flags & MNT_NOEXEC) || (path->mnt->mnt_sb->s_iflags & SB_I_NOEXEC); } +EXPORT_SYMBOL_GPL(path_noexec); #ifdef CONFIG_USELIB /* diff --git a/fs/fcntl.c b/fs/fcntl.c index 9a582f35919a4..78f977cb8b5d7 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -85,6 +85,7 @@ int setfl(int fd, struct file *filp, unsigned long arg) out: return error; } +EXPORT_SYMBOL_GPL(setfl); static void f_modown(struct file *filp, struct pid *pid, enum pid_type type, int force) diff --git a/fs/file_table.c b/fs/file_table.c index 7a3b4a7f68086..b5e3bcd3f255f 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -162,6 +162,7 @@ struct file *alloc_empty_file(int flags, const struct cred *cred) } return ERR_PTR(-ENFILE); } +EXPORT_SYMBOL_GPL(alloc_empty_file); /* * Variant of alloc_empty_file() that doesn't check and modify nr_files. diff --git a/fs/namespace.c b/fs/namespace.c index a256f0f9c6c09..5d6b3e723e913 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -431,6 +431,7 @@ void __mnt_drop_write(struct vfsmount *mnt) mnt_dec_writers(real_mount(mnt)); preempt_enable(); } +EXPORT_SYMBOL_GPL(__mnt_drop_write); /** * mnt_drop_write - give up write access to a mount @@ -797,6 +798,7 @@ int is_current_mnt_ns(struct vfsmount *mnt) { return check_mnt(real_mount(mnt)); } +EXPORT_SYMBOL_GPL(is_current_mnt_ns); /* * vfsmount lock must be held for write @@ -1998,6 +2000,7 @@ int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg, } return 0; } +EXPORT_SYMBOL_GPL(iterate_mounts); static void lock_mnt_tree(struct mount *mnt) { diff --git a/fs/notify/group.c b/fs/notify/group.c index a4a4b1c64d32a..86dc2efb1850c 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -100,6 +100,7 @@ void fsnotify_get_group(struct fsnotify_group *group) { refcount_inc(&group->refcnt); } +EXPORT_SYMBOL_GPL(fsnotify_get_group); /* * Drop a reference to a group. Free it if it's through. diff --git a/fs/open.c b/fs/open.c index b3fbb4300fc96..31ce3773cb6c8 100644 --- a/fs/open.c +++ b/fs/open.c @@ -65,6 +65,7 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, inode_unlock(dentry->d_inode); return ret; } +EXPORT_SYMBOL_GPL(do_truncate); long vfs_truncate(const struct path *path, loff_t length) { diff --git a/fs/read_write.c b/fs/read_write.c index 0066acb6b380d..ab3109d5106bf 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -503,6 +503,7 @@ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) inc_syscr(current); return ret; } +EXPORT_SYMBOL_GPL(vfs_read); static ssize_t new_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos) { @@ -613,6 +614,7 @@ ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_ file_end_write(file); return ret; } +EXPORT_SYMBOL_GPL(vfs_write); /* file_ppos returns &file->f_pos or NULL if file is stream */ static inline loff_t *file_ppos(struct file *file) diff --git a/fs/splice.c b/fs/splice.c index 55b5356262085..c13ac0fbac318 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -763,6 +763,7 @@ long do_splice_from(struct pipe_inode_info *pipe, struct file *out, return warn_unsupported(out, "write"); return out->f_op->splice_write(pipe, out, ppos, len, flags); } +EXPORT_SYMBOL_GPL(do_splice_from); /* * Attempt to initiate a splice from a file to a pipe. @@ -787,6 +788,7 @@ long do_splice_to(struct file *in, loff_t *ppos, return warn_unsupported(in, "read"); return in->f_op->splice_read(in, ppos, pipe, len, flags); } +EXPORT_SYMBOL_GPL(do_splice_to); /** * splice_direct_to_actor - splices data directly between two non-pipes diff --git a/fs/xattr.c b/fs/xattr.c index cd7a563e8bcd4..7d989d57b0f0d 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -360,6 +360,7 @@ vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value, *xattr_value = value; return error; } +EXPORT_SYMBOL_GPL(vfs_getxattr_alloc); ssize_t __vfs_getxattr(struct dentry *dentry, struct inode *inode, const char *name, diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c index 52483df4b7f9c..5a3affee7a9bc 100644 --- a/kernel/locking/lockdep.c +++ b/kernel/locking/lockdep.c @@ -207,6 +207,7 @@ inline struct lock_class *lockdep_hlock_class(struct held_lock *hlock) */ return lock_classes + class_idx; } +EXPORT_SYMBOL_GPL(lockdep_hlock_class); #define hlock_class(hlock) lockdep_hlock_class(hlock) #ifdef CONFIG_LOCK_STAT diff --git a/kernel/task_work.c b/kernel/task_work.c index e9316198c64bf..3b9dda2ce21a8 100644 --- a/kernel/task_work.c +++ b/kernel/task_work.c @@ -164,3 +164,4 @@ void task_work_run(void) } while (work); } } +EXPORT_SYMBOL_GPL(task_work_run); diff --git a/security/security.c b/security/security.c index 8ea826ea6167e..a1e4bb7eaccea 100644 --- a/security/security.c +++ b/security/security.c @@ -1109,6 +1109,7 @@ int security_path_rmdir(const struct path *dir, struct dentry *dentry) return 0; return call_int_hook(path_rmdir, 0, dir, dentry); } +EXPORT_SYMBOL_GPL(security_path_rmdir); int security_path_unlink(const struct path *dir, struct dentry *dentry) { @@ -1125,6 +1126,7 @@ int security_path_symlink(const struct path *dir, struct dentry *dentry, return 0; return call_int_hook(path_symlink, 0, dir, dentry, old_name); } +EXPORT_SYMBOL_GPL(security_path_symlink); int security_path_link(struct dentry *old_dentry, const struct path *new_dir, struct dentry *new_dentry) @@ -1133,6 +1135,7 @@ int security_path_link(struct dentry *old_dentry, const struct path *new_dir, return 0; return call_int_hook(path_link, 0, old_dentry, new_dir, new_dentry); } +EXPORT_SYMBOL_GPL(security_path_link); int security_path_rename(const struct path *old_dir, struct dentry *old_dentry, const struct path *new_dir, struct dentry *new_dentry, @@ -1160,6 +1163,7 @@ int security_path_truncate(const struct path *path) return 0; return call_int_hook(path_truncate, 0, path); } +EXPORT_SYMBOL_GPL(security_path_truncate); int security_path_chmod(const struct path *path, umode_t mode) { @@ -1167,6 +1171,7 @@ int security_path_chmod(const struct path *path, umode_t mode) return 0; return call_int_hook(path_chmod, 0, path, mode); } +EXPORT_SYMBOL_GPL(security_path_chmod); int security_path_chown(const struct path *path, kuid_t uid, kgid_t gid) { @@ -1174,6 +1179,7 @@ int security_path_chown(const struct path *path, kuid_t uid, kgid_t gid) return 0; return call_int_hook(path_chown, 0, path, uid, gid); } +EXPORT_SYMBOL_GPL(security_path_chown); int security_path_chroot(const struct path *path) { @@ -1274,6 +1280,7 @@ int security_inode_permission(struct inode *inode, int mask) return 0; return call_int_hook(inode_permission, 0, inode, mask); } +EXPORT_SYMBOL_GPL(security_inode_permission); int security_inode_setattr(struct dentry *dentry, struct iattr *attr) { @@ -1466,6 +1473,7 @@ int security_file_permission(struct file *file, int mask) return fsnotify_perm(file, mask); } +EXPORT_SYMBOL_GPL(security_file_permission); int security_file_alloc(struct file *file) { From 167eaf735386e809e72c4b8915777c45a7c22453 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Fri, 3 Feb 2023 19:22:15 -0600 Subject: [PATCH 05/65] merge: aufs https://github.com/sfjro/aufs-standalone/commit/72075d1d0bd59c94529348883e810410318674e5 Signed-off-by: Robert Nelson --- Documentation/ABI/testing/debugfs-aufs | 55 + Documentation/ABI/testing/sysfs-aufs | 31 + Documentation/filesystems/aufs/README | 395 ++++ .../filesystems/aufs/design/01intro.txt | 171 ++ .../filesystems/aufs/design/02struct.txt | 258 +++ .../filesystems/aufs/design/03atomic_open.txt | 85 + .../filesystems/aufs/design/03lookup.txt | 113 + .../filesystems/aufs/design/04branch.txt | 74 + .../filesystems/aufs/design/05wbr_policy.txt | 64 + .../filesystems/aufs/design/06dirren.dot | 31 + .../filesystems/aufs/design/06dirren.txt | 102 + .../filesystems/aufs/design/06fhsm.txt | 120 + .../filesystems/aufs/design/06mmap.txt | 72 + .../filesystems/aufs/design/06xattr.txt | 96 + .../filesystems/aufs/design/07export.txt | 58 + .../filesystems/aufs/design/08shwh.txt | 52 + .../filesystems/aufs/design/10dynop.txt | 47 + fs/aufs/Kconfig | 199 ++ fs/aufs/Makefile | 46 + fs/aufs/aufs.h | 62 + fs/aufs/branch.c | 1427 ++++++++++++ fs/aufs/branch.h | 364 ++++ fs/aufs/conf.mk | 40 + fs/aufs/cpup.c | 1447 +++++++++++++ fs/aufs/cpup.h | 100 + fs/aufs/dbgaufs.c | 526 +++++ fs/aufs/dbgaufs.h | 53 + fs/aufs/dcsub.c | 225 ++ fs/aufs/dcsub.h | 137 ++ fs/aufs/debug.c | 444 ++++ fs/aufs/debug.h | 226 ++ fs/aufs/dentry.c | 1162 ++++++++++ fs/aufs/dentry.h | 268 +++ fs/aufs/dinfo.c | 554 +++++ fs/aufs/dir.c | 763 +++++++ fs/aufs/dir.h | 134 ++ fs/aufs/dirren.c | 1315 +++++++++++ fs/aufs/dirren.h | 140 ++ fs/aufs/dynop.c | 368 ++++ fs/aufs/dynop.h | 77 + fs/aufs/export.c | 836 +++++++ fs/aufs/f_op.c | 775 +++++++ fs/aufs/fhsm.c | 427 ++++ fs/aufs/file.c | 863 ++++++++ fs/aufs/file.h | 342 +++ fs/aufs/finfo.c | 149 ++ fs/aufs/fsctx.c | 1242 +++++++++++ fs/aufs/fstype.h | 401 ++++ fs/aufs/hbl.h | 65 + fs/aufs/hfsnotify.c | 288 +++ fs/aufs/hfsplus.c | 60 + fs/aufs/hnotify.c | 715 ++++++ fs/aufs/i_op.c | 1501 +++++++++++++ fs/aufs/i_op_add.c | 936 ++++++++ fs/aufs/i_op_del.c | 520 +++++ fs/aufs/i_op_ren.c | 1256 +++++++++++ fs/aufs/iinfo.c | 286 +++ fs/aufs/inode.c | 529 +++++ fs/aufs/inode.h | 698 ++++++ fs/aufs/ioctl.c | 220 ++ fs/aufs/lcnt.h | 186 ++ fs/aufs/loop.c | 148 ++ fs/aufs/loop.h | 55 + fs/aufs/magic.mk | 31 + fs/aufs/module.c | 273 +++ fs/aufs/module.h | 166 ++ fs/aufs/mvdown.c | 706 ++++++ fs/aufs/opts.c | 1032 +++++++++ fs/aufs/opts.h | 263 +++ fs/aufs/plink.c | 516 +++++ fs/aufs/poll.c | 51 + fs/aufs/posix_acl.c | 105 + fs/aufs/procfs.c | 170 ++ fs/aufs/rdu.c | 384 ++++ fs/aufs/rwsem.h | 77 + fs/aufs/sbinfo.c | 316 +++ fs/aufs/super.c | 868 ++++++++ fs/aufs/super.h | 592 +++++ fs/aufs/sysaufs.c | 93 + fs/aufs/sysaufs.h | 102 + fs/aufs/sysfs.c | 374 ++++ fs/aufs/sysrq.c | 149 ++ fs/aufs/vdir.c | 896 ++++++++ fs/aufs/vfsub.c | 889 ++++++++ fs/aufs/vfsub.h | 354 +++ fs/aufs/wbr_policy.c | 830 +++++++ fs/aufs/whout.c | 1064 +++++++++ fs/aufs/whout.h | 86 + fs/aufs/wkq.c | 372 ++++ fs/aufs/wkq.h | 89 + fs/aufs/xattr.c | 356 +++ fs/aufs/xino.c | 1926 +++++++++++++++++ include/uapi/linux/aufs_type.h | 452 ++++ 93 files changed, 37981 insertions(+) create mode 100644 Documentation/ABI/testing/debugfs-aufs create mode 100644 Documentation/ABI/testing/sysfs-aufs create mode 100644 Documentation/filesystems/aufs/README create mode 100644 Documentation/filesystems/aufs/design/01intro.txt create mode 100644 Documentation/filesystems/aufs/design/02struct.txt create mode 100644 Documentation/filesystems/aufs/design/03atomic_open.txt create mode 100644 Documentation/filesystems/aufs/design/03lookup.txt create mode 100644 Documentation/filesystems/aufs/design/04branch.txt create mode 100644 Documentation/filesystems/aufs/design/05wbr_policy.txt create mode 100644 Documentation/filesystems/aufs/design/06dirren.dot create mode 100644 Documentation/filesystems/aufs/design/06dirren.txt create mode 100644 Documentation/filesystems/aufs/design/06fhsm.txt create mode 100644 Documentation/filesystems/aufs/design/06mmap.txt create mode 100644 Documentation/filesystems/aufs/design/06xattr.txt create mode 100644 Documentation/filesystems/aufs/design/07export.txt create mode 100644 Documentation/filesystems/aufs/design/08shwh.txt create mode 100644 Documentation/filesystems/aufs/design/10dynop.txt create mode 100644 fs/aufs/Kconfig create mode 100644 fs/aufs/Makefile create mode 100644 fs/aufs/aufs.h create mode 100644 fs/aufs/branch.c create mode 100644 fs/aufs/branch.h create mode 100644 fs/aufs/conf.mk create mode 100644 fs/aufs/cpup.c create mode 100644 fs/aufs/cpup.h create mode 100644 fs/aufs/dbgaufs.c create mode 100644 fs/aufs/dbgaufs.h create mode 100644 fs/aufs/dcsub.c create mode 100644 fs/aufs/dcsub.h create mode 100644 fs/aufs/debug.c create mode 100644 fs/aufs/debug.h create mode 100644 fs/aufs/dentry.c create mode 100644 fs/aufs/dentry.h create mode 100644 fs/aufs/dinfo.c create mode 100644 fs/aufs/dir.c create mode 100644 fs/aufs/dir.h create mode 100644 fs/aufs/dirren.c create mode 100644 fs/aufs/dirren.h create mode 100644 fs/aufs/dynop.c create mode 100644 fs/aufs/dynop.h create mode 100644 fs/aufs/export.c create mode 100644 fs/aufs/f_op.c create mode 100644 fs/aufs/fhsm.c create mode 100644 fs/aufs/file.c create mode 100644 fs/aufs/file.h create mode 100644 fs/aufs/finfo.c create mode 100644 fs/aufs/fsctx.c create mode 100644 fs/aufs/fstype.h create mode 100644 fs/aufs/hbl.h create mode 100644 fs/aufs/hfsnotify.c create mode 100644 fs/aufs/hfsplus.c create mode 100644 fs/aufs/hnotify.c create mode 100644 fs/aufs/i_op.c create mode 100644 fs/aufs/i_op_add.c create mode 100644 fs/aufs/i_op_del.c create mode 100644 fs/aufs/i_op_ren.c create mode 100644 fs/aufs/iinfo.c create mode 100644 fs/aufs/inode.c create mode 100644 fs/aufs/inode.h create mode 100644 fs/aufs/ioctl.c create mode 100644 fs/aufs/lcnt.h create mode 100644 fs/aufs/loop.c create mode 100644 fs/aufs/loop.h create mode 100644 fs/aufs/magic.mk create mode 100644 fs/aufs/module.c create mode 100644 fs/aufs/module.h create mode 100644 fs/aufs/mvdown.c create mode 100644 fs/aufs/opts.c create mode 100644 fs/aufs/opts.h create mode 100644 fs/aufs/plink.c create mode 100644 fs/aufs/poll.c create mode 100644 fs/aufs/posix_acl.c create mode 100644 fs/aufs/procfs.c create mode 100644 fs/aufs/rdu.c create mode 100644 fs/aufs/rwsem.h create mode 100644 fs/aufs/sbinfo.c create mode 100644 fs/aufs/super.c create mode 100644 fs/aufs/super.h create mode 100644 fs/aufs/sysaufs.c create mode 100644 fs/aufs/sysaufs.h create mode 100644 fs/aufs/sysfs.c create mode 100644 fs/aufs/sysrq.c create mode 100644 fs/aufs/vdir.c create mode 100644 fs/aufs/vfsub.c create mode 100644 fs/aufs/vfsub.h create mode 100644 fs/aufs/wbr_policy.c create mode 100644 fs/aufs/whout.c create mode 100644 fs/aufs/whout.h create mode 100644 fs/aufs/wkq.c create mode 100644 fs/aufs/wkq.h create mode 100644 fs/aufs/xattr.c create mode 100644 fs/aufs/xino.c create mode 100644 include/uapi/linux/aufs_type.h diff --git a/Documentation/ABI/testing/debugfs-aufs b/Documentation/ABI/testing/debugfs-aufs new file mode 100644 index 0000000000000..45b739879d769 --- /dev/null +++ b/Documentation/ABI/testing/debugfs-aufs @@ -0,0 +1,55 @@ +What: /debug/aufs/si_/ +Date: March 2009 +Contact: J. R. Okajima +Description: + Under /debug/aufs, a directory named si_ is created + per aufs mount, where is a unique id generated + internally. + +What: /debug/aufs/si_/plink +Date: Apr 2013 +Contact: J. R. Okajima +Description: + It has three lines and shows the information about the + pseudo-link. The first line is a single number + representing a number of buckets. The second line is a + number of pseudo-links per buckets (separated by a + blank). The last line is a single number representing a + total number of psedo-links. + When the aufs mount option 'noplink' is specified, it + will show "1\n0\n0\n". + +What: /debug/aufs/si_/xib +Date: March 2009 +Contact: J. R. Okajima +Description: + It shows the consumed blocks by xib (External Inode Number + Bitmap), its block size and file size. + When the aufs mount option 'noxino' is specified, it + will be empty. About XINO files, see the aufs manual. + +What: /debug/aufs/si_/xi +Date: March 2009 +Contact: J. R. Okajima +Description: + It shows the consumed blocks by xino (External Inode Number + Translation Table), its link count, block size and file + size. + Due to the file size limit, there may exist multiple + xino files per branch. In this case, "-N" is added to + the filename and it corresponds to the index of the + internal xino array. "-0" is omitted. + When the aufs mount option 'noxino' is specified, Those + entries won't exist. About XINO files, see the aufs + manual. + +What: /debug/aufs/si_/xigen +Date: March 2009 +Contact: J. R. Okajima +Description: + It shows the consumed blocks by xigen (External Inode + Generation Table), its block size and file size. + If CONFIG_AUFS_EXPORT is disabled, this entry will not + be created. + When the aufs mount option 'noxino' is specified, it + will be empty. About XINO files, see the aufs manual. diff --git a/Documentation/ABI/testing/sysfs-aufs b/Documentation/ABI/testing/sysfs-aufs new file mode 100644 index 0000000000000..48500c0569e65 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-aufs @@ -0,0 +1,31 @@ +What: /sys/fs/aufs/si_/ +Date: March 2009 +Contact: J. R. Okajima +Description: + Under /sys/fs/aufs, a directory named si_ is created + per aufs mount, where is a unique id generated + internally. + +What: /sys/fs/aufs/si_/br +Date: March 2009 +Contact: J. R. Okajima +Description: + It shows the abolute path of a member directory (which + is called branch) in aufs, and its permission. + +What: /sys/fs/aufs/si_/brid +Date: July 2013 +Contact: J. R. Okajima +Description: + It shows the id of a member directory (which is called + branch) in aufs. + +What: /sys/fs/aufs/si_/xi_path +Date: March 2009 +Contact: J. R. Okajima +Description: + It shows the abolute path of XINO (External Inode Number + Bitmap, Translation Table and Generation Table) file + even if it is the default path. + When the aufs mount option 'noxino' is specified, it + will be empty. About XINO files, see the aufs manual. diff --git a/Documentation/filesystems/aufs/README b/Documentation/filesystems/aufs/README new file mode 100644 index 0000000000000..03d608a9ef89b --- /dev/null +++ b/Documentation/filesystems/aufs/README @@ -0,0 +1,395 @@ + +Aufs5 -- advanced multi layered unification filesystem version 5.x +http://aufs.sf.net +Junjiro R. Okajima + + +0. Introduction +---------------------------------------- +In the early days, aufs was entirely re-designed and re-implemented +Unionfs Version 1.x series. Adding many original ideas, approaches, +improvements and implementations, it became totally different from +Unionfs while keeping the basic features. +Later, Unionfs Version 2.x series began taking some of the same +approaches to aufs1's. +Unionfs was being developed by Professor Erez Zadok at Stony Brook +University and his team. + +Aufs5 supports linux-v5.0 and later, If you want older kernel version +support, +- for linux-v4.x series, try aufs4-linux.git or aufs4-standalone.git +- for linux-v3.x series, try aufs3-linux.git or aufs3-standalone.git +- for linux-v2.6.16 and later, try aufs2-2.6.git, aufs2-standalone.git + or aufs1 from CVS on SourceForge. + +Note: it becomes clear that "Aufs was rejected. Let's give it up." + According to Christoph Hellwig, linux rejects all union-type + filesystems but UnionMount. + + +PS. Al Viro seems have a plan to merge aufs as well as overlayfs and + UnionMount, and he pointed out an issue around a directory mutex + lock and aufs addressed it. But it is still unsure whether aufs will + be merged (or any other union solution). + + + +1. Features +---------------------------------------- +- unite several directories into a single virtual filesystem. The member + directory is called as a branch. +- you can specify the permission flags to the branch, which are 'readonly', + 'readwrite' and 'whiteout-able.' +- by upper writable branch, internal copyup and whiteout, files/dirs on + readonly branch are modifiable logically. +- dynamic branch manipulation, add, del. +- etc... + +Also there are many enhancements in aufs, such as: +- test only the highest one for the directory permission (dirperm1) +- copyup on open (coo=) +- 'move' policy for copy-up between two writable branches, after + checking free space. +- xattr, acl +- readdir(3) in userspace. +- keep inode number by external inode number table +- keep the timestamps of file/dir in internal copyup operation +- seekable directory, supporting NFS readdir. +- whiteout is hardlinked in order to reduce the consumption of inodes + on branch +- do not copyup, nor create a whiteout when it is unnecessary +- revert a single systemcall when an error occurs in aufs +- remount interface instead of ioctl +- maintain /etc/mtab by an external command, /sbin/mount.aufs. +- loopback mounted filesystem as a branch +- kernel thread for removing the dir who has a plenty of whiteouts +- support copyup sparse file (a file which has a 'hole' in it) +- default permission flags for branches +- selectable permission flags for ro branch, whether whiteout can + exist or not +- export via NFS. +- support /fs/aufs and /aufs. +- support multiple writable branches, some policies to select one + among multiple writable branches. +- a new semantics for link(2) and rename(2) to support multiple + writable branches. +- no glibc changes are required. +- pseudo hardlink (hardlink over branches) +- allow a direct access manually to a file on branch, e.g. bypassing aufs. + including NFS or remote filesystem branch. +- userspace wrapper for pathconf(3)/fpathconf(3) with _PC_LINK_MAX. +- and more... + +Currently these features are dropped temporary from aufs5. +See design/08plan.txt in detail. +- nested mount, i.e. aufs as readonly no-whiteout branch of another aufs + (robr) +- statistics of aufs thread (/sys/fs/aufs/stat) + +Features or just an idea in the future (see also design/*.txt), +- reorder the branch index without del/re-add. +- permanent xino files for NFSD +- an option for refreshing the opened files after add/del branches +- light version, without branch manipulation. (unnecessary?) +- copyup in userspace +- inotify in userspace +- readv/writev + + +2. Download +---------------------------------------- +There are three GIT trees for aufs5, aufs5-linux.git, +aufs5-standalone.git, and aufs-util.git. Note that there is no "5" in +"aufs-util.git." +While the aufs-util is always necessary, you need either of aufs5-linux +or aufs5-standalone. + +The aufs5-linux tree includes the whole linux mainline GIT tree, +git://git.kernel.org/.../torvalds/linux.git. +And you cannot select CONFIG_AUFS_FS=m for this version, eg. you cannot +build aufs5 as an external kernel module. +Several extra patches are not included in this tree. Only +aufs5-standalone tree contains them. They are described in the later +section "Configuration and Compilation." + +On the other hand, the aufs5-standalone tree has only aufs source files +and necessary patches, and you can select CONFIG_AUFS_FS=m. +But you need to apply all aufs patches manually. + +You will find GIT branches whose name is in form of "aufs5.x" where "x" +represents the linux kernel version, "linux-5.x". For instance, +"aufs5.0" is for linux-5.0. For latest "linux-5.x-rcN", use +"aufs5.x-rcN" branch. + +o aufs5-linux tree +$ git clone --reference /your/linux/git/tree \ + git://github.com/sfjro/aufs5-linux.git aufs5-linux.git +- if you don't have linux GIT tree, then remove "--reference ..." +$ cd aufs5-linux.git +$ git checkout origin/aufs5.0 + +Or You may want to directly git-pull aufs into your linux GIT tree, and +leave the patch-work to GIT. +$ cd /your/linux/git/tree +$ git remote add aufs5 git://github.com/sfjro/aufs5-linux.git +$ git fetch aufs5 +$ git checkout -b my5.0 v5.0 +$ (add your local change...) +$ git pull aufs5 aufs5.0 +- now you have v5.0 + your_changes + aufs5.0 in you my5.0 branch. +- you may need to solve some conflicts between your_changes and + aufs5.0. in this case, git-rerere is recommended so that you can + solve the similar conflicts automatically when you upgrade to 5.1 or + later in the future. + +o aufs5-standalone tree +$ git clone git://github.com/sfjro/aufs5-standalone.git aufs5-standalone.git +$ cd aufs5-standalone.git +$ git checkout origin/aufs5.0 + +o aufs-util tree +$ git clone git://git.code.sf.net/p/aufs/aufs-util aufs-util.git +- note that the public aufs-util.git is on SourceForge instead of + GitHUB. +$ cd aufs-util.git +$ git checkout origin/aufs5.0 + +Note: The 5.x-rcN branch is to be used with `rc' kernel versions ONLY. +The minor version number, 'x' in '5.x', of aufs may not always +follow the minor version number of the kernel. +Because changes in the kernel that cause the use of a new +minor version number do not always require changes to aufs-util. + +Since aufs-util has its own minor version number, you may not be +able to find a GIT branch in aufs-util for your kernel's +exact minor version number. +In this case, you should git-checkout the branch for the +nearest lower number. + +For (an unreleased) example: +If you are using "linux-5.10" and the "aufs5.10" branch +does not exist in aufs-util repository, then "aufs5.9", "aufs5.8" +or something numerically smaller is the branch for your kernel. + +Also you can view all branches by + $ git branch -a + + +3. Configuration and Compilation +---------------------------------------- +Make sure you have git-checkout'ed the correct branch. + +For aufs5-linux tree, +- enable CONFIG_AUFS_FS. +- set other aufs configurations if necessary. + +For aufs5-standalone tree, +There are several ways to build. + +1. +- apply ./aufs5-kbuild.patch to your kernel source files. +- apply ./aufs5-base.patch too. +- apply ./aufs5-mmap.patch too. +- apply ./aufs5-standalone.patch too, if you have a plan to set + CONFIG_AUFS_FS=m. otherwise you don't need ./aufs5-standalone.patch. +- copy ./{Documentation,fs,include/uapi/linux/aufs_type.h} files to your + kernel source tree. Never copy $PWD/include/uapi/linux/Kbuild. +- enable CONFIG_AUFS_FS, you can select either + =m or =y. +- and build your kernel as usual. +- install the built kernel. +- install the header files too by "make headers_install" to the + directory where you specify. By default, it is $PWD/usr. + "make help" shows a brief note for headers_install. +- and reboot your system. + +2. +- module only (CONFIG_AUFS_FS=m). +- apply ./aufs5-base.patch to your kernel source files. +- apply ./aufs5-mmap.patch too. +- apply ./aufs5-standalone.patch too. +- build your kernel, don't forget "make headers_install", and reboot. +- edit ./config.mk and set other aufs configurations if necessary. + Note: You should read $PWD/fs/aufs/Kconfig carefully which describes + every aufs configurations. +- build the module by simple "make". +- you can specify ${KDIR} make variable which points to your kernel + source tree. +- install the files + + run "make install" to install the aufs module, or copy the built + $PWD/aufs.ko to /lib/modules/... and run depmod -a (or reboot simply). + + run "make install_headers" (instead of headers_install) to install + the modified aufs header file (you can specify DESTDIR which is + available in aufs standalone version's Makefile only), or copy + $PWD/usr/include/linux/aufs_type.h to /usr/include/linux or wherever + you like manually. By default, the target directory is $PWD/usr. +- no need to apply aufs5-kbuild.patch, nor copying source files to your + kernel source tree. + +Note: The header file aufs_type.h is necessary to build aufs-util + as well as "make headers_install" in the kernel source tree. + headers_install is subject to be forgotten, but it is essentially + necessary, not only for building aufs-util. + You may not meet problems without headers_install in some older + version though. + +And then, +- read README in aufs-util, build and install it +- note that your distribution may contain an obsoleted version of + aufs_type.h in /usr/include/linux or something. When you build aufs + utilities, make sure that your compiler refers the correct aufs header + file which is built by "make headers_install." +- if you want to use readdir(3) in userspace or pathconf(3) wrapper, + then run "make install_ulib" too. And refer to the aufs manual in + detail. + +There several other patches in aufs5-standalone.git. They are all +optional. When you meet some problems, they will help you. +- aufs5-loopback.patch + Supports a nested loopback mount in a branch-fs. This patch is + unnecessary until aufs produces a message like "you may want to try + another patch for loopback file". +- vfs-ino.patch + Modifies a system global kernel internal function get_next_ino() in + order to stop assigning 0 for an inode-number. Not directly related to + aufs, but recommended generally. +- tmpfs-idr.patch + Keeps the tmpfs inode number as the lowest value. Effective to reduce + the size of aufs XINO files for tmpfs branch. Also it prevents the + duplication of inode number, which is important for backup tools and + other utilities. When you find aufs XINO files for tmpfs branch + growing too much, try this patch. +- lockdep-debug.patch + Because aufs is not only an ordinary filesystem (callee of VFS), but + also a caller of VFS functions for branch filesystems, subclassing of + the internal locks for LOCKDEP is necessary. LOCKDEP is a debugging + feature of linux kernel. If you enable CONFIG_LOCKDEP, then you will + need to apply this debug patch to expand several constant values. + If you don't know what LOCKDEP is, then you don't have apply this + patch. + + +4. Usage +---------------------------------------- +At first, make sure aufs-util are installed, and please read the aufs +manual, aufs.5 in aufs-util.git tree. +$ man -l aufs.5 + +And then, +$ mkdir /tmp/rw /tmp/aufs +# mount -t aufs -o br=/tmp/rw:${HOME} none /tmp/aufs + +Here is another example. The result is equivalent. +# mount -t aufs -o br=/tmp/rw=rw:${HOME}=ro none /tmp/aufs + Or +# mount -t aufs -o br:/tmp/rw none /tmp/aufs +# mount -o remount,append:${HOME} /tmp/aufs + +Then, you can see whole tree of your home dir through /tmp/aufs. If +you modify a file under /tmp/aufs, the one on your home directory is +not affected, instead the same named file will be newly created under +/tmp/rw. And all of your modification to a file will be applied to +the one under /tmp/rw. This is called the file based Copy on Write +(COW) method. +Aufs mount options are described in aufs.5. +If you run chroot or something and make your aufs as a root directory, +then you need to customize the shutdown script. See the aufs manual in +detail. + +Additionally, there are some sample usages of aufs which are a +diskless system with network booting, and LiveCD over NFS. +See sample dir in CVS tree on SourceForge. + + +5. Contact +---------------------------------------- +When you have any problems or strange behaviour in aufs, please let me +know with: +- /proc/mounts (instead of the output of mount(8)) +- /sys/module/aufs/* +- /sys/fs/aufs/* (if you have them) +- /debug/aufs/* (if you have them) +- linux kernel version + if your kernel is not plain, for example modified by distributor, + the url where i can download its source is necessary too. +- aufs version which was printed at loading the module or booting the + system, instead of the date you downloaded. +- configuration (define/undefine CONFIG_AUFS_xxx) +- kernel configuration or /proc/config.gz (if you have it) +- LSM (linux security module, if you are using) +- behaviour which you think to be incorrect +- actual operation, reproducible one is better +- mailto: aufs-users at lists.sourceforge.net + +Usually, I don't watch the Public Areas(Bugs, Support Requests, Patches, +and Feature Requests) on SourceForge. Please join and write to +aufs-users ML. + + +6. Acknowledgements +---------------------------------------- +Thanks to everyone who have tried and are using aufs, whoever +have reported a bug or any feedback. + +Especially donators: +Tomas Matejicek(slax.org) made a donation (much more than once). + Since Apr 2010, Tomas M (the author of Slax and Linux Live + scripts) is making "doubling" donations. + Unfortunately I cannot list all of the donators, but I really + appreciate. + It ends Aug 2010, but the ordinary donation URL is still available. + +Dai Itasaka made a donation (2007/8). +Chuck Smith made a donation (2008/4, 10 and 12). +Henk Schoneveld made a donation (2008/9). +Chih-Wei Huang, ASUS, CTC donated Eee PC 4G (2008/10). +Francois Dupoux made a donation (2008/11). +Bruno Cesar Ribas and Luis Carlos Erpen de Bona, C3SL serves public + aufs2 GIT tree (2009/2). +William Grant made a donation (2009/3). +Patrick Lane made a donation (2009/4). +The Mail Archive (mail-archive.com) made donations (2009/5). +Nippy Networks (Ed Wildgoose) made a donation (2009/7). +New Dream Network, LLC (www.dreamhost.com) made a donation (2009/11). +Pavel Pronskiy made a donation (2011/2). +Iridium and Inmarsat satellite phone retailer (www.mailasail.com), Nippy + Networks (Ed Wildgoose) made a donation for hardware (2011/3). +Max Lekomcev (DOM-TV project) made a donation (2011/7, 12, 2012/3, 6 and +11). +Sam Liddicott made a donation (2011/9). +Era Scarecrow made a donation (2013/4). +Bor Ratajc made a donation (2013/4). +Alessandro Gorreta made a donation (2013/4). +POIRETTE Marc made a donation (2013/4). +Alessandro Gorreta made a donation (2013/4). +lauri kasvandik made a donation (2013/5). +"pemasu from Finland" made a donation (2013/7). +The Parted Magic Project made a donation (2013/9 and 11). +Pavel Barta made a donation (2013/10). +Nikolay Pertsev made a donation (2014/5). +James B made a donation (2014/7, 2015/7, and 2021/12). +Stefano Di Biase made a donation (2014/8). +Daniel Epellei made a donation (2015/1). +OmegaPhil made a donation (2016/1, 2018/4). +Tomasz Szewczyk made a donation (2016/4). +James Burry made a donation (2016/12). +Carsten Rose made a donation (2018/9). +Porteus Kiosk made a donation (2018/10). +Enya Quetzalli Gomez Rodriguez made a donation (2022/5). + +Thank you very much. +Donations are always, including future donations, very important and +helpful for me to keep on developing aufs. + + +7. +---------------------------------------- +If you are an experienced user, no explanation is needed. Aufs is +just a linux filesystem. + + +Enjoy! + +# Local variables: ; +# mode: text; +# End: ; diff --git a/Documentation/filesystems/aufs/design/01intro.txt b/Documentation/filesystems/aufs/design/01intro.txt new file mode 100644 index 0000000000000..26949e1167e14 --- /dev/null +++ b/Documentation/filesystems/aufs/design/01intro.txt @@ -0,0 +1,171 @@ + +# Copyright (C) 2005-2021 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +Introduction +---------------------------------------- + +aufs [ei ju: ef es] | /ey-yoo-ef-es/ | [a u f s] +1. abbrev. for "advanced multi-layered unification filesystem". +2. abbrev. for "another unionfs". +3. abbrev. for "auf das" in German which means "on the" in English. + Ex. "Butter aufs Brot"(G) means "butter onto bread"(E). + But "Filesystem aufs Filesystem" is hard to understand. +4. abbrev. for "African Urban Fashion Show". + +AUFS is a filesystem with features: +- multi layered stackable unification filesystem, the member directory + is called as a branch. +- branch permission and attribute, 'readonly', 'real-readonly', + 'readwrite', 'whiteout-able', 'link-able whiteout', etc. and their + combination. +- internal "file copy-on-write". +- logical deletion, whiteout. +- dynamic branch manipulation, adding, deleting and changing permission. +- allow bypassing aufs, user's direct branch access. +- external inode number translation table and bitmap which maintains the + persistent aufs inode number. +- seekable directory, including NFS readdir. +- file mapping, mmap and sharing pages. +- pseudo-link, hardlink over branches. +- loopback mounted filesystem as a branch. +- several policies to select one among multiple writable branches. +- revert a single systemcall when an error occurs in aufs. +- and more... + + +Multi Layered Stackable Unification Filesystem +---------------------------------------------------------------------- +Most people already knows what it is. +It is a filesystem which unifies several directories and provides a +merged single directory. When users access a file, the access will be +passed/re-directed/converted (sorry, I am not sure which English word is +correct) to the real file on the member filesystem. The member +filesystem is called 'lower filesystem' or 'branch' and has a mode +'readonly' and 'readwrite.' And the deletion for a file on the lower +readonly branch is handled by creating 'whiteout' on the upper writable +branch. + +On LKML, there have been discussions about UnionMount (Jan Blunck, +Bharata B Rao and Valerie Aurora) and Unionfs (Erez Zadok). They took +different approaches to implement the merged-view. +The former tries putting it into VFS, and the latter implements as a +separate filesystem. +(If I misunderstand about these implementations, please let me know and +I shall correct it. Because it is a long time ago when I read their +source files last time). + +UnionMount's approach will be able to small, but may be hard to share +branches between several UnionMount since the whiteout in it is +implemented in the inode on branch filesystem and always +shared. According to Bharata's post, readdir does not seems to be +finished yet. +There are several missing features known in this implementations such as +- for users, the inode number may change silently. eg. copy-up. +- link(2) may break by copy-up. +- read(2) may get an obsoleted filedata (fstat(2) too). +- fcntl(F_SETLK) may be broken by copy-up. +- unnecessary copy-up may happen, for example mmap(MAP_PRIVATE) after + open(O_RDWR). + +In linux-3.18, "overlay" filesystem (formerly known as "overlayfs") was +merged into mainline. This is another implementation of UnionMount as a +separated filesystem. All the limitations and known problems which +UnionMount are equally inherited to "overlay" filesystem. + +Unionfs has a longer history. When I started implementing a stackable +filesystem (Aug 2005), it already existed. It has virtual super_block, +inode, dentry and file objects and they have an array pointing lower +same kind objects. After contributing many patches for Unionfs, I +re-started my project AUFS (Jun 2006). + +In AUFS, the structure of filesystem resembles to Unionfs, but I +implemented my own ideas, approaches and enhancements and it became +totally different one. + +Comparing DM snapshot and fs based implementation +- the number of bytes to be copied between devices is much smaller. +- the type of filesystem must be one and only. +- the fs must be writable, no readonly fs, even for the lower original + device. so the compression fs will not be usable. but if we use + loopback mount, we may address this issue. + for instance, + mount /cdrom/squashfs.img /sq + losetup /sq/ext2.img + losetup /somewhere/cow + dmsetup "snapshot /dev/loop0 /dev/loop1 ..." +- it will be difficult (or needs more operations) to extract the + difference between the original device and COW. +- DM snapshot-merge may help a lot when users try merging. in the + fs-layer union, users will use rsync(1). + +You may want to read my old paper "Filesystems in LiveCD" +(http://aufs.sourceforge.net/aufs2/report/sq/sq.pdf). + + +Several characters/aspects/persona of aufs +---------------------------------------------------------------------- + +Aufs has several characters, aspects or persona. +1. a filesystem, callee of VFS helper +2. sub-VFS, caller of VFS helper for branches +3. a virtual filesystem which maintains persistent inode number +4. reader/writer of files on branches such like an application + +1. Callee of VFS Helper +As an ordinary linux filesystem, aufs is a callee of VFS. For instance, +unlink(2) from an application reaches sys_unlink() kernel function and +then vfs_unlink() is called. vfs_unlink() is one of VFS helper and it +calls filesystem specific unlink operation. Actually aufs implements the +unlink operation but it behaves like a redirector. + +2. Caller of VFS Helper for Branches +aufs_unlink() passes the unlink request to the branch filesystem as if +it were called from VFS. So the called unlink operation of the branch +filesystem acts as usual. As a caller of VFS helper, aufs should handle +every necessary pre/post operation for the branch filesystem. +- acquire the lock for the parent dir on a branch +- lookup in a branch +- revalidate dentry on a branch +- mnt_want_write() for a branch +- vfs_unlink() for a branch +- mnt_drop_write() for a branch +- release the lock on a branch + +3. Persistent Inode Number +One of the most important issue for a filesystem is to maintain inode +numbers. This is particularly important to support exporting a +filesystem via NFS. Aufs is a virtual filesystem which doesn't have a +backend block device for its own. But some storage is necessary to +keep and maintain the inode numbers. It may be a large space and may not +suit to keep in memory. Aufs rents some space from its first writable +branch filesystem (by default) and creates file(s) on it. These files +are created by aufs internally and removed soon (currently) keeping +opened. +Note: Because these files are removed, they are totally gone after + unmounting aufs. It means the inode numbers are not persistent + across unmount or reboot. I have a plan to make them really + persistent which will be important for aufs on NFS server. + +4. Read/Write Files Internally (copy-on-write) +Because a branch can be readonly, when you write a file on it, aufs will +"copy-up" it to the upper writable branch internally. And then write the +originally requested thing to the file. Generally kernel doesn't +open/read/write file actively. In aufs, even a single write may cause a +internal "file copy". This behaviour is very similar to cp(1) command. + +Some people may think it is better to pass such work to user space +helper, instead of doing in kernel space. Actually I am still thinking +about it. But currently I have implemented it in kernel space. diff --git a/Documentation/filesystems/aufs/design/02struct.txt b/Documentation/filesystems/aufs/design/02struct.txt new file mode 100644 index 0000000000000..e2d88759b6dd0 --- /dev/null +++ b/Documentation/filesystems/aufs/design/02struct.txt @@ -0,0 +1,258 @@ + +# Copyright (C) 2005-2021 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +Basic Aufs Internal Structure + +Superblock/Inode/Dentry/File Objects +---------------------------------------------------------------------- +As like an ordinary filesystem, aufs has its own +superblock/inode/dentry/file objects. All these objects have a +dynamically allocated array and store the same kind of pointers to the +lower filesystem, branch. +For example, when you build a union with one readwrite branch and one +readonly, mounted /au, /rw and /ro respectively. +- /au = /rw + /ro +- /ro/fileA exists but /rw/fileA + +Aufs lookup operation finds /ro/fileA and gets dentry for that. These +pointers are stored in a aufs dentry. The array in aufs dentry will be, +- [0] = NULL (because /rw/fileA doesn't exist) +- [1] = /ro/fileA + +This style of an array is essentially same to the aufs +superblock/inode/dentry/file objects. + +Because aufs supports manipulating branches, ie. add/delete/change +branches dynamically, these objects has its own generation. When +branches are changed, the generation in aufs superblock is +incremented. And a generation in other object are compared when it is +accessed. When a generation in other objects are obsoleted, aufs +refreshes the internal array. + + +Superblock +---------------------------------------------------------------------- +Additionally aufs superblock has some data for policies to select one +among multiple writable branches, XIB files, pseudo-links and kobject. +See below in detail. +About the policies which supports copy-down a directory, see +wbr_policy.txt too. + + +Branch and XINO(External Inode Number Translation Table) +---------------------------------------------------------------------- +Every branch has its own xino (external inode number translation table) +file. The xino file is created and unlinked by aufs internally. When two +members of a union exist on the same filesystem, they share the single +xino file. +The struct of a xino file is simple, just a sequence of aufs inode +numbers which is indexed by the lower inode number. +In the above sample, assume the inode number of /ro/fileA is i111 and +aufs assigns the inode number i999 for fileA. Then aufs writes 999 as +4(8) bytes at 111 * 4(8) bytes offset in the xino file. + +When the inode numbers are not contiguous, the xino file will be sparse +which has a hole in it and doesn't consume as much disk space as it +might appear. If your branch filesystem consumes disk space for such +holes, then you should specify 'xino=' option at mounting aufs. + +Aufs has a mount option to free the disk blocks for such holes in XINO +files on tmpfs or ramdisk. But it is not so effective actually. If you +meet a problem of disk shortage due to XINO files, then you should try +"tmpfs-ino.patch" (and "vfs-ino.patch" too) in aufs4-standalone.git. +The patch localizes the assignment inumbers per tmpfs-mount and avoid +the holes in XINO files. + +Also a writable branch has three kinds of "whiteout bases". All these +are existed when the branch is joined to aufs, and their names are +whiteout-ed doubly, so that users will never see their names in aufs +hierarchy. +1. a regular file which will be hardlinked to all whiteouts. +2. a directory to store a pseudo-link. +3. a directory to store an "orphan"-ed file temporary. + +1. Whiteout Base + When you remove a file on a readonly branch, aufs handles it as a + logical deletion and creates a whiteout on the upper writable branch + as a hardlink of this file in order not to consume inode on the + writable branch. +2. Pseudo-link Dir + See below, Pseudo-link. +3. Step-Parent Dir + When "fileC" exists on the lower readonly branch only and it is + opened and removed with its parent dir, and then user writes + something into it, then aufs copies-up fileC to this + directory. Because there is no other dir to store fileC. After + creating a file under this dir, the file is unlinked. + +Because aufs supports manipulating branches, ie. add/delete/change +dynamically, a branch has its own id. When the branch order changes, +aufs finds the new index by searching the branch id. + + +Pseudo-link +---------------------------------------------------------------------- +Assume "fileA" exists on the lower readonly branch only and it is +hardlinked to "fileB" on the branch. When you write something to fileA, +aufs copies-up it to the upper writable branch. Additionally aufs +creates a hardlink under the Pseudo-link Directory of the writable +branch. The inode of a pseudo-link is kept in aufs super_block as a +simple list. If fileB is read after unlinking fileA, aufs returns +filedata from the pseudo-link instead of the lower readonly +branch. Because the pseudo-link is based upon the inode, to keep the +inode number by xino (see above) is essentially necessary. + +All the hardlinks under the Pseudo-link Directory of the writable branch +should be restored in a proper location later. Aufs provides a utility +to do this. The userspace helpers executed at remounting and unmounting +aufs by default. +During this utility is running, it puts aufs into the pseudo-link +maintenance mode. In this mode, only the process which began the +maintenance mode (and its child processes) is allowed to operate in +aufs. Some other processes which are not related to the pseudo-link will +be allowed to run too, but the rest have to return an error or wait +until the maintenance mode ends. If a process already acquires an inode +mutex (in VFS), it has to return an error. + + +XIB(external inode number bitmap) +---------------------------------------------------------------------- +Addition to the xino file per a branch, aufs has an external inode number +bitmap in a superblock object. It is also an internal file such like a +xino file. +It is a simple bitmap to mark whether the aufs inode number is in-use or +not. +To reduce the file I/O, aufs prepares a single memory page to cache xib. + +As well as XINO files, aufs has a feature to truncate/refresh XIB to +reduce the number of consumed disk blocks for these files. + + +Virtual or Vertical Dir, and Readdir in Userspace +---------------------------------------------------------------------- +In order to support multiple layers (branches), aufs readdir operation +constructs a virtual dir block on memory. For readdir, aufs calls +vfs_readdir() internally for each dir on branches, merges their entries +with eliminating the whiteout-ed ones, and sets it to file (dir) +object. So the file object has its entry list until it is closed. The +entry list will be updated when the file position is zero and becomes +obsoleted. This decision is made in aufs automatically. + +The dynamically allocated memory block for the name of entries has a +unit of 512 bytes (by default) and stores the names contiguously (no +padding). Another block for each entry is handled by kmem_cache too. +During building dir blocks, aufs creates hash list and judging whether +the entry is whiteouted by its upper branch or already listed. +The merged result is cached in the corresponding inode object and +maintained by a customizable life-time option. + +Some people may call it can be a security hole or invite DoS attack +since the opened and once readdir-ed dir (file object) holds its entry +list and becomes a pressure for system memory. But I'd say it is similar +to files under /proc or /sys. The virtual files in them also holds a +memory page (generally) while they are opened. When an idea to reduce +memory for them is introduced, it will be applied to aufs too. +For those who really hate this situation, I've developed readdir(3) +library which operates this merging in userspace. You just need to set +LD_PRELOAD environment variable, and aufs will not consume no memory in +kernel space for readdir(3). + + +Workqueue +---------------------------------------------------------------------- +Aufs sometimes requires privilege access to a branch. For instance, +in copy-up/down operation. When a user process is going to make changes +to a file which exists in the lower readonly branch only, and the mode +of one of ancestor directories may not be writable by a user +process. Here aufs copy-up the file with its ancestors and they may +require privilege to set its owner/group/mode/etc. +This is a typical case of a application character of aufs (see +Introduction). + +Aufs uses workqueue synchronously for this case. It creates its own +workqueue. The workqueue is a kernel thread and has privilege. Aufs +passes the request to call mkdir or write (for example), and wait for +its completion. This approach solves a problem of a signal handler +simply. +If aufs didn't adopt the workqueue and changed the privilege of the +process, then the process may receive the unexpected SIGXFSZ or other +signals. + +Also aufs uses the system global workqueue ("events" kernel thread) too +for asynchronous tasks, such like handling inotify/fsnotify, re-creating a +whiteout base and etc. This is unrelated to a privilege. +Most of aufs operation tries acquiring a rw_semaphore for aufs +superblock at the beginning, at the same time waits for the completion +of all queued asynchronous tasks. + + +Whiteout +---------------------------------------------------------------------- +The whiteout in aufs is very similar to Unionfs's. That is represented +by its filename. UnionMount takes an approach of a file mode, but I am +afraid several utilities (find(1) or something) will have to support it. + +Basically the whiteout represents "logical deletion" which stops aufs to +lookup further, but also it represents "dir is opaque" which also stop +further lookup. + +In aufs, rmdir(2) and rename(2) for dir uses whiteout alternatively. +In order to make several functions in a single systemcall to be +revertible, aufs adopts an approach to rename a directory to a temporary +unique whiteouted name. +For example, in rename(2) dir where the target dir already existed, aufs +renames the target dir to a temporary unique whiteouted name before the +actual rename on a branch, and then handles other actions (make it opaque, +update the attributes, etc). If an error happens in these actions, aufs +simply renames the whiteouted name back and returns an error. If all are +succeeded, aufs registers a function to remove the whiteouted unique +temporary name completely and asynchronously to the system global +workqueue. + + +Copy-up +---------------------------------------------------------------------- +It is a well-known feature or concept. +When user modifies a file on a readonly branch, aufs operate "copy-up" +internally and makes change to the new file on the upper writable branch. +When the trigger systemcall does not update the timestamps of the parent +dir, aufs reverts it after copy-up. + + +Move-down (aufs3.9 and later) +---------------------------------------------------------------------- +"Copy-up" is one of the essential feature in aufs. It copies a file from +the lower readonly branch to the upper writable branch when a user +changes something about the file. +"Move-down" is an opposite action of copy-up. Basically this action is +ran manually instead of automatically and internally. +For desgin and implementation, aufs has to consider these issues. +- whiteout for the file may exist on the lower branch. +- ancestor directories may not exist on the lower branch. +- diropq for the ancestor directories may exist on the upper branch. +- free space on the lower branch will reduce. +- another access to the file may happen during moving-down, including + UDBA (see "Revalidate Dentry and UDBA"). +- the file should not be hard-linked nor pseudo-linked. they should be + handled by auplink utility later. + +Sometimes users want to move-down a file from the upper writable branch +to the lower readonly or writable branch. For instance, +- the free space of the upper writable branch is going to run out. +- create a new intermediate branch between the upper and lower branch. +- etc. + +For this purpose, use "aumvdown" command in aufs-util.git. diff --git a/Documentation/filesystems/aufs/design/03atomic_open.txt b/Documentation/filesystems/aufs/design/03atomic_open.txt new file mode 100644 index 0000000000000..30b0cd351a752 --- /dev/null +++ b/Documentation/filesystems/aufs/design/03atomic_open.txt @@ -0,0 +1,85 @@ + +# Copyright (C) 2015-2021 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +Support for a branch who has its ->atomic_open() +---------------------------------------------------------------------- +The filesystems who implement its ->atomic_open() are not majority. For +example NFSv4 does, and aufs should call NFSv4 ->atomic_open, +particularly for open(O_CREAT|O_EXCL, 0400) case. Other than +->atomic_open(), NFSv4 returns an error for this open(2). While I am not +sure whether all filesystems who have ->atomic_open() behave like this, +but NFSv4 surely returns the error. + +In order to support ->atomic_open() for aufs, there are a few +approaches. + +A. Introduce aufs_atomic_open() + - calls one of VFS:do_last(), lookup_open() or atomic_open() for + branch fs. +B. Introduce aufs_atomic_open() calling create, open and chmod. this is + an aufs user Pip Cet's approach + - calls aufs_create(), VFS finish_open() and notify_change(). + - pass fake-mode to finish_open(), and then correct the mode by + notify_change(). +C. Extend aufs_open() to call branch fs's ->atomic_open() + - no aufs_atomic_open(). + - aufs_lookup() registers the TID to an aufs internal object. + - aufs_create() does nothing when the matching TID is registered, but + registers the mode. + - aufs_open() calls branch fs's ->atomic_open() when the matching + TID is registered. +D. Extend aufs_open() to re-try branch fs's ->open() with superuser's + credential + - no aufs_atomic_open(). + - aufs_create() registers the TID to an internal object. this info + represents "this process created this file just now." + - when aufs gets EACCES from branch fs's ->open(), then confirm the + registered TID and re-try open() with superuser's credential. + +Pros and cons for each approach. + +A. + - straightforward but highly depends upon VFS internal. + - the atomic behavaiour is kept. + - some of parameters such as nameidata are hard to reproduce for + branch fs. + - large overhead. +B. + - easy to implement. + - the atomic behavaiour is lost. +C. + - the atomic behavaiour is kept. + - dirty and tricky. + - VFS checks whether the file is created correctly after calling + ->create(), which means this approach doesn't work. +D. + - easy to implement. + - the atomic behavaiour is lost. + - to open a file with superuser's credential and give it to a user + process is a bad idea, since the file object keeps the credential + in it. It may affect LSM or something. This approach doesn't work + either. + +The approach A is ideal, but it hard to implement. So here is a +variation of A, which is to be implemented. + +A-1. Introduce aufs_atomic_open() + - calls branch fs ->atomic_open() if exists. otherwise calls + vfs_create() and finish_open(). + - the demerit is that the several checks after branch fs + ->atomic_open() are lost. in the ordinary case, the checks are + done by VFS:do_last(), lookup_open() and atomic_open(). some can + be implemented in aufs, but not all I am afraid. diff --git a/Documentation/filesystems/aufs/design/03lookup.txt b/Documentation/filesystems/aufs/design/03lookup.txt new file mode 100644 index 0000000000000..c1eacd18b8f3c --- /dev/null +++ b/Documentation/filesystems/aufs/design/03lookup.txt @@ -0,0 +1,113 @@ + +# Copyright (C) 2005-2021 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +Lookup in a Branch +---------------------------------------------------------------------- +Since aufs has a character of sub-VFS (see Introduction), it operates +lookup for branches as VFS does. It may be a heavy work. But almost all +lookup operation in aufs is the simplest case, ie. lookup only an entry +directly connected to its parent. Digging down the directory hierarchy +is unnecessary. VFS has a function lookup_one_len() for that use, and +aufs calls it. + +When a branch is a remote filesystem, aufs basically relies upon its +->d_revalidate(), also aufs forces the hardest revalidate tests for +them. +For d_revalidate, aufs implements three levels of revalidate tests. See +"Revalidate Dentry and UDBA" in detail. + + +Test Only the Highest One for the Directory Permission (dirperm1 option) +---------------------------------------------------------------------- +Let's try case study. +- aufs has two branches, upper readwrite and lower readonly. + /au = /rw + /ro +- "dirA" exists under /ro, but /rw. and its mode is 0700. +- user invoked "chmod a+rx /au/dirA" +- the internal copy-up is activated and "/rw/dirA" is created and its + permission bits are set to world readable. +- then "/au/dirA" becomes world readable? + +In this case, /ro/dirA is still 0700 since it exists in readonly branch, +or it may be a natively readonly filesystem. If aufs respects the lower +branch, it should not respond readdir request from other users. But user +allowed it by chmod. Should really aufs rejects showing the entries +under /ro/dirA? + +To be honest, I don't have a good solution for this case. So aufs +implements 'dirperm1' and 'nodirperm1' mount options, and leave it to +users. +When dirperm1 is specified, aufs checks only the highest one for the +directory permission, and shows the entries. Otherwise, as usual, checks +every dir existing on all branches and rejects the request. + +As a side effect, dirperm1 option improves the performance of aufs +because the number of permission check is reduced when the number of +branch is many. + + +Revalidate Dentry and UDBA (User's Direct Branch Access) +---------------------------------------------------------------------- +Generally VFS helpers re-validate a dentry as a part of lookup. +0. digging down the directory hierarchy. +1. lock the parent dir by its i_mutex. +2. lookup the final (child) entry. +3. revalidate it. +4. call the actual operation (create, unlink, etc.) +5. unlock the parent dir + +If the filesystem implements its ->d_revalidate() (step 3), then it is +called. Actually aufs implements it and checks the dentry on a branch is +still valid. +But it is not enough. Because aufs has to release the lock for the +parent dir on a branch at the end of ->lookup() (step 2) and +->d_revalidate() (step 3) while the i_mutex of the aufs dir is still +held by VFS. +If the file on a branch is changed directly, eg. bypassing aufs, after +aufs released the lock, then the subsequent operation may cause +something unpleasant result. + +This situation is a result of VFS architecture, ->lookup() and +->d_revalidate() is separated. But I never say it is wrong. It is a good +design from VFS's point of view. It is just not suitable for sub-VFS +character in aufs. + +Aufs supports such case by three level of revalidation which is +selectable by user. +1. Simple Revalidate + Addition to the native flow in VFS's, confirm the child-parent + relationship on the branch just after locking the parent dir on the + branch in the "actual operation" (step 4). When this validation + fails, aufs returns EBUSY. ->d_revalidate() (step 3) in aufs still + checks the validation of the dentry on branches. +2. Monitor Changes Internally by Inotify/Fsnotify + Addition to above, in the "actual operation" (step 4) aufs re-lookup + the dentry on the branch, and returns EBUSY if it finds different + dentry. + Additionally, aufs sets the inotify/fsnotify watch for every dir on branches + during it is in cache. When the event is notified, aufs registers a + function to kernel 'events' thread by schedule_work(). And the + function sets some special status to the cached aufs dentry and inode + private data. If they are not cached, then aufs has nothing to + do. When the same file is accessed through aufs (step 0-3) later, + aufs will detect the status and refresh all necessary data. + In this mode, aufs has to ignore the event which is fired by aufs + itself. +3. No Extra Validation + This is the simplest test and doesn't add any additional revalidation + test, and skip the revalidation in step 4. It is useful and improves + aufs performance when system surely hide the aufs branches from user, + by over-mounting something (or another method). diff --git a/Documentation/filesystems/aufs/design/04branch.txt b/Documentation/filesystems/aufs/design/04branch.txt new file mode 100644 index 0000000000000..95ec7e06abd95 --- /dev/null +++ b/Documentation/filesystems/aufs/design/04branch.txt @@ -0,0 +1,74 @@ + +# Copyright (C) 2005-2021 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +Branch Manipulation + +Since aufs supports dynamic branch manipulation, ie. add/remove a branch +and changing its permission/attribute, there are a lot of works to do. + + +Add a Branch +---------------------------------------------------------------------- +o Confirm the adding dir exists outside of aufs, including loopback + mount, and its various attributes. +o Initialize the xino file and whiteout bases if necessary. + See struct.txt. + +o Check the owner/group/mode of the directory + When the owner/group/mode of the adding directory differs from the + existing branch, aufs issues a warning because it may impose a + security risk. + For example, when a upper writable branch has a world writable empty + top directory, a malicious user can create any files on the writable + branch directly, like copy-up and modify manually. If something like + /etc/{passwd,shadow} exists on the lower readonly branch but the upper + writable branch, and the writable branch is world-writable, then a + malicious guy may create /etc/passwd on the writable branch directly + and the infected file will be valid in aufs. + I am afraid it can be a security issue, but aufs can do nothing except + producing a warning. + + +Delete a Branch +---------------------------------------------------------------------- +o Confirm the deleting branch is not busy + To be general, there is one merit to adopt "remount" interface to + manipulate branches. It is to discard caches. At deleting a branch, + aufs checks the still cached (and connected) dentries and inodes. If + there are any, then they are all in-use. An inode without its + corresponding dentry can be alive alone (for example, inotify/fsnotify case). + + For the cached one, aufs checks whether the same named entry exists on + other branches. + If the cached one is a directory, because aufs provides a merged view + to users, as long as one dir is left on any branch aufs can show the + dir to users. In this case, the branch can be removed from aufs. + Otherwise aufs rejects deleting the branch. + + If any file on the deleting branch is opened by aufs, then aufs + rejects deleting. + + +Modify the Permission of a Branch +---------------------------------------------------------------------- +o Re-initialize or remove the xino file and whiteout bases if necessary. + See struct.txt. + +o rw --> ro: Confirm the modifying branch is not busy + Aufs rejects the request if any of these conditions are true. + - a file on the branch is mmap-ed. + - a regular file on the branch is opened for write and there is no + same named entry on the upper branch. diff --git a/Documentation/filesystems/aufs/design/05wbr_policy.txt b/Documentation/filesystems/aufs/design/05wbr_policy.txt new file mode 100644 index 0000000000000..f05a002eec9fc --- /dev/null +++ b/Documentation/filesystems/aufs/design/05wbr_policy.txt @@ -0,0 +1,64 @@ + +# Copyright (C) 2005-2021 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +Policies to Select One among Multiple Writable Branches +---------------------------------------------------------------------- +When the number of writable branch is more than one, aufs has to decide +the target branch for file creation or copy-up. By default, the highest +writable branch which has the parent (or ancestor) dir of the target +file is chosen (top-down-parent policy). +By user's request, aufs implements some other policies to select the +writable branch, for file creation several policies, round-robin, +most-free-space, and other policies. For copy-up, top-down-parent, +bottom-up-parent, bottom-up and others. + +As expected, the round-robin policy selects the branch in circular. When +you have two writable branches and creates 10 new files, 5 files will be +created for each branch. mkdir(2) systemcall is an exception. When you +create 10 new directories, all will be created on the same branch. +And the most-free-space policy selects the one which has most free +space among the writable branches. The amount of free space will be +checked by aufs internally, and users can specify its time interval. + +The policies for copy-up is more simple, +top-down-parent is equivalent to the same named on in create policy, +bottom-up-parent selects the writable branch where the parent dir +exists and the nearest upper one from the copyup-source, +bottom-up selects the nearest upper writable branch from the +copyup-source, regardless the existence of the parent dir. + +There are some rules or exceptions to apply these policies. +- If there is a readonly branch above the policy-selected branch and + the parent dir is marked as opaque (a variation of whiteout), or the + target (creating) file is whiteout-ed on the upper readonly branch, + then the result of the policy is ignored and the target file will be + created on the nearest upper writable branch than the readonly branch. +- If there is a writable branch above the policy-selected branch and + the parent dir is marked as opaque or the target file is whiteouted + on the branch, then the result of the policy is ignored and the target + file will be created on the highest one among the upper writable + branches who has diropq or whiteout. In case of whiteout, aufs removes + it as usual. +- link(2) and rename(2) systemcalls are exceptions in every policy. + They try selecting the branch where the source exists as possible + since copyup a large file will take long time. If it can't be, + ie. the branch where the source exists is readonly, then they will + follow the copyup policy. +- There is an exception for rename(2) when the target exists. + If the rename target exists, aufs compares the index of the branches + where the source and the target exists and selects the higher + one. If the selected branch is readonly, then aufs follows the + copyup policy. diff --git a/Documentation/filesystems/aufs/design/06dirren.dot b/Documentation/filesystems/aufs/design/06dirren.dot new file mode 100644 index 0000000000000..2d62bb6dd55fd --- /dev/null +++ b/Documentation/filesystems/aufs/design/06dirren.dot @@ -0,0 +1,31 @@ + +// to view this graph, run dot(1) command in GRAPHVIZ. + +digraph G { +node [shape=box]; +whinfo [label="detailed info file\n(lower_brid_root-hinum, h_inum, namelen, old name)"]; + +node [shape=oval]; + +aufs_rename -> whinfo [label="store/remove"]; + +node [shape=oval]; +inode_list [label="h_inum list in branch\ncache"]; + +node [shape=box]; +whinode [label="h_inum list file"]; + +node [shape=oval]; +brmgmt [label="br_add/del/mod/umount"]; + +brmgmt -> inode_list [label="create/remove"]; +brmgmt -> whinode [label="load/store"]; + +inode_list -> whinode [style=dashed,dir=both]; + +aufs_rename -> inode_list [label="add/del"]; + +aufs_lookup -> inode_list [label="search"]; + +aufs_lookup -> whinfo [label="load/remove"]; +} diff --git a/Documentation/filesystems/aufs/design/06dirren.txt b/Documentation/filesystems/aufs/design/06dirren.txt new file mode 100644 index 0000000000000..b8bf235f6d437 --- /dev/null +++ b/Documentation/filesystems/aufs/design/06dirren.txt @@ -0,0 +1,102 @@ + +# Copyright (C) 2017-2021 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +Special handling for renaming a directory (DIRREN) +---------------------------------------------------------------------- +First, let's assume we have a simple usecase. + +- /u = /rw + /ro +- /rw/dirA exists +- /ro/dirA and /ro/dirA/file exist too +- there is no dirB on both branches +- a user issues rename("dirA", "dirB") + +Now, what should aufs behave against this rename(2)? +There are a few possible cases. + +A. returns EROFS. + since dirA exists on a readonly branch which cannot be renamed. +B. returns EXDEV. + it is possible to copy-up dirA (only the dir itself), but the child + entries ("file" in this case) should not be. it must be a bad + approach to copy-up recursively. +C. returns a success. + even the branch /ro is readonly, aufs tries renaming it. Obviously it + is a violation of aufs' policy. +D. construct an extra information which indicates that /ro/dirA should + be handled as the name of dirB. + overlayfs has a similar feature called REDIRECT. + +Until now, aufs implements the case B only which returns EXDEV, and +expects the userspace application behaves like mv(1) which tries +issueing rename(2) recursively. + +A new aufs feature called DIRREN is introduced which implements the case +D. There are several "extra information" added. + +1. detailed info per renamed directory + path: /rw/dirB/$AUFS_WH_DR_INFO_PFX. +2. the inode-number list of directories on a branch + path: /rw/dirB/$AUFS_WH_DR_BRHINO + +The filename of "detailed info per directory" represents the lower +branch, and its format is +- a type of the branch id + one of these. + + uuid (not implemented yet) + + fsid + + dev +- the inode-number of the branch root dir + +And it contains these info in a single regular file. +- magic number +- branch's inode-number of the logically renamed dir +- the name of the before-renamed dir + +The "detailed info per directory" file is created in aufs rename(2), and +loaded in any lookup. +The info is considered in lookup for the matching case only. Here +"matching" means that the root of branch (in the info filename) is same +to the current looking-up branch. After looking-up the before-renamed +name, the inode-number is compared. And the matched dentry is used. + +The "inode-number list of directories" is a regular file which contains +simply the inode-numbers on the branch. The file is created or updated +in removing the branch, and loaded in adding the branch. Its lifetime is +equal to the branch. +The list is referred in lookup, and when the current target inode is +found in the list, the aufs tries loading the "detailed info per +directory" and get the changed and valid name of the dir. + +Theoretically these "extra informaiton" may be able to be put into XATTR +in the dir inode. But aufs doesn't choose this way because +1. XATTR may not be supported by the branch (or its configuration) +2. XATTR may have its size limit. +3. XATTR may be less easy to convert than a regular file, when the + format of the info is changed in the future. +At the same time, I agree that the regular file approach is much slower +than XATTR approach. So, in the future, aufs may take the XATTR or other +better approach. + +This DIRREN feature is enabled by aufs configuration, and is activated +by a new mount option. + +For the more complicated case, there is a work with UDBA option, which +is to dected the direct access to the branches (by-passing aufs) and to +maintain the cashes in aufs. Since a single cached aufs dentry may +contains two names, before- and after-rename, the name comparision in +UDBA handler may not work correctly. In this case, the behaviour will be +equivalen to udba=reval case. diff --git a/Documentation/filesystems/aufs/design/06fhsm.txt b/Documentation/filesystems/aufs/design/06fhsm.txt new file mode 100644 index 0000000000000..44024476eae02 --- /dev/null +++ b/Documentation/filesystems/aufs/design/06fhsm.txt @@ -0,0 +1,120 @@ + +# Copyright (C) 2011-2021 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +File-based Hierarchical Storage Management (FHSM) +---------------------------------------------------------------------- +Hierarchical Storage Management (or HSM) is a well-known feature in the +storage world. Aufs provides this feature as file-based with multiple +writable branches, based upon the principle of "Colder, the Lower". +Here the word "colder" means that the less used files, and "lower" means +that the position in the order of the stacked branches vertically. +These multiple writable branches are prioritized, ie. the topmost one +should be the fastest drive and be used heavily. + +o Characters in aufs FHSM story +- aufs itself and a new branch attribute. +- a new ioctl interface to move-down and to establish a connection with + the daemon ("move-down" is a converse of "copy-up"). +- userspace tool and daemon. + +The userspace daemon establishes a connection with aufs and waits for +the notification. The notified information is very similar to struct +statfs containing the number of consumed blocks and inodes. +When the consumed blocks/inodes of a branch exceeds the user-specified +upper watermark, the daemon activates its move-down process until the +consumed blocks/inodes reaches the user-specified lower watermark. + +The actual move-down is done by aufs based upon the request from +user-space since we need to maintain the inode number and the internal +pointer arrays in aufs. + +Currently aufs FHSM handles the regular files only. Additionally they +must not be hard-linked nor pseudo-linked. + + +o Cowork of aufs and the user-space daemon + During the userspace daemon established the connection, aufs sends a + small notification to it whenever aufs writes something into the + writable branch. But it may cost high since aufs issues statfs(2) + internally. So user can specify a new option to cache the + info. Actually the notification is controlled by these factors. + + the specified cache time. + + classified as "force" by aufs internally. + Until the specified time expires, aufs doesn't send the info + except the forced cases. When aufs decide forcing, the info is always + notified to userspace. + For example, the number of free inodes is generally large enough and + the shortage of it happens rarely. So aufs doesn't force the + notification when creating a new file, directory and others. This is + the typical case which aufs doesn't force. + When aufs writes the actual filedata and the files consumes any of new + blocks, the aufs forces notifying. + + +o Interfaces in aufs +- New branch attribute. + + fhsm + Specifies that the branch is managed by FHSM feature. In other word, + participant in the FHSM. + When nofhsm is set to the branch, it will not be the source/target + branch of the move-down operation. This attribute is set + independently from coo and moo attributes, and if you want full + FHSM, you should specify them as well. +- New mount option. + + fhsm_sec + Specifies a second to suppress many less important info to be + notified. +- New ioctl. + + AUFS_CTL_FHSM_FD + create a new file descriptor which userspace can read the notification + (a subset of struct statfs) from aufs. +- Module parameter 'brs' + It has to be set to 1. Otherwise the new mount option 'fhsm' will not + be set. +- mount helpers /sbin/mount.aufs and /sbin/umount.aufs + When there are two or more branches with fhsm attributes, + /sbin/mount.aufs invokes the user-space daemon and /sbin/umount.aufs + terminates it. As a result of remounting and branch-manipulation, the + number of branches with fhsm attribute can be one. In this case, + /sbin/mount.aufs will terminate the user-space daemon. + + +Finally the operation is done as these steps in kernel-space. +- make sure that, + + no one else is using the file. + + the file is not hard-linked. + + the file is not pseudo-linked. + + the file is a regular file. + + the parent dir is not opaqued. +- find the target writable branch. +- make sure the file is not whiteout-ed by the upper (than the target) + branch. +- make the parent dir on the target branch. +- mutex lock the inode on the branch. +- unlink the whiteout on the target branch (if exists). +- lookup and create the whiteout-ed temporary name on the target branch. +- copy the file as the whiteout-ed temporary name on the target branch. +- rename the whiteout-ed temporary name to the original name. +- unlink the file on the source branch. +- maintain the internal pointer array and the external inode number + table (XINO). +- maintain the timestamps and other attributes of the parent dir and the + file. + +And of course, in every step, an error may happen. So the operation +should restore the original file state after an error happens. diff --git a/Documentation/filesystems/aufs/design/06mmap.txt b/Documentation/filesystems/aufs/design/06mmap.txt new file mode 100644 index 0000000000000..e7c9a22599252 --- /dev/null +++ b/Documentation/filesystems/aufs/design/06mmap.txt @@ -0,0 +1,72 @@ + +# Copyright (C) 2005-2021 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +mmap(2) -- File Memory Mapping +---------------------------------------------------------------------- +In aufs, the file-mapped pages are handled by a branch fs directly, no +interaction with aufs. It means aufs_mmap() calls the branch fs's +->mmap(). +This approach is simple and good, but there is one problem. +Under /proc, several entries show the mmapped files by its path (with +device and inode number), and the printed path will be the path on the +branch fs's instead of virtual aufs's. +This is not a problem in most cases, but some utilities lsof(1) (and its +user) may expect the path on aufs. + +To address this issue, aufs adds a new member called vm_prfile in struct +vm_area_struct (and struct vm_region). The original vm_file points to +the file on the branch fs in order to handle everything correctly as +usual. The new vm_prfile points to a virtual file in aufs, and the +show-functions in procfs refers to vm_prfile if it is set. +Also we need to maintain several other places where touching vm_file +such like +- fork()/clone() copies vma and the reference count of vm_file is + incremented. +- merging vma maintains the ref count too. + +This is not a good approach. It just fakes the printed path. But it +leaves all behaviour around f_mapping unchanged. This is surely an +advantage. +Actually aufs had adopted another complicated approach which calls +generic_file_mmap() and handles struct vm_operations_struct. In this +approach, aufs met a hard problem and I could not solve it without +switching the approach. + +There may be one more another approach which is +- bind-mount the branch-root onto the aufs-root internally +- grab the new vfsmount (ie. struct mount) +- lazy-umount the branch-root internally +- in open(2) the aufs-file, open the branch-file with the hidden + vfsmount (instead of the original branch's vfsmount) +- ideally this "bind-mount and lazy-umount" should be done atomically, + but it may be possible from userspace by the mount helper. + +Adding the internal hidden vfsmount and using it in opening a file, the +file path under /proc will be printed correctly. This approach looks +smarter, but is not possible I am afraid. +- aufs-root may be bind-mount later. when it happens, another hidden + vfsmount will be required. +- it is hard to get the chance to bind-mount and lazy-umount + + in kernel-space, FS can have vfsmount in open(2) via + file->f_path, and aufs can know its vfsmount. But several locks are + already acquired, and if aufs tries to bind-mount and lazy-umount + here, then it may cause a deadlock. + + in user-space, bind-mount doesn't invoke the mount helper. +- since /proc shows dev and ino, aufs has to give vma these info. it + means a new member vm_prinode will be necessary. this is essentially + equivalent to vm_prfile described above. + +I have to give up this "looks-smater" approach. diff --git a/Documentation/filesystems/aufs/design/06xattr.txt b/Documentation/filesystems/aufs/design/06xattr.txt new file mode 100644 index 0000000000000..4b112bc733dbc --- /dev/null +++ b/Documentation/filesystems/aufs/design/06xattr.txt @@ -0,0 +1,96 @@ + +# Copyright (C) 2014-2021 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Listing XATTR/EA and getting the value +---------------------------------------------------------------------- +For the inode standard attributes (owner, group, timestamps, etc.), aufs +shows the values from the topmost existing file. This behaviour is good +for the non-dir entries since the bahaviour exactly matches the shown +information. But for the directories, aufs considers all the same named +entries on the lower branches. Which means, if one of the lower entry +rejects readdir call, then aufs returns an error even if the topmost +entry allows it. This behaviour is necessary to respect the branch fs's +security, but can make users confused since the user-visible standard +attributes don't match the behaviour. +To address this issue, aufs has a mount option called dirperm1 which +checks the permission for the topmost entry only, and ignores the lower +entry's permission. + +A similar issue can happen around XATTR. +getxattr(2) and listxattr(2) families behave as if dirperm1 option is +always set. Otherwise these very unpleasant situation would happen. +- listxattr(2) may return the duplicated entries. +- users may not be able to remove or reset the XATTR forever, + + +XATTR/EA support in the internal (copy,move)-(up,down) +---------------------------------------------------------------------- +Generally the extended attributes of inode are categorized as these. +- "security" for LSM and capability. +- "system" for posix ACL, 'acl' mount option is required for the branch + fs generally. +- "trusted" for userspace, CAP_SYS_ADMIN is required. +- "user" for userspace, 'user_xattr' mount option is required for the + branch fs generally. + +Moreover there are some other categories. Aufs handles these rather +unpopular categories as the ordinary ones, ie. there is no special +condition nor exception. + +In copy-up, the support for XATTR on the dst branch may differ from the +src branch. In this case, the copy-up operation will get an error and +the original user operation which triggered the copy-up will fail. It +can happen that even all copy-up will fail. +When both of src and dst branches support XATTR and if an error occurs +during copying XATTR, then the copy-up should fail obviously. That is a +good reason and aufs should return an error to userspace. But when only +the src branch support that XATTR, aufs should not return an error. +For example, the src branch supports ACL but the dst branch doesn't +because the dst branch may natively un-support it or temporary +un-support it due to "noacl" mount option. Of course, the dst branch fs +may NOT return an error even if the XATTR is not supported. It is +totally up to the branch fs. + +Anyway when the aufs internal copy-up gets an error from the dst branch +fs, then aufs tries removing the just copied entry and returns the error +to the userspace. The worst case of this situation will be all copy-up +will fail. + +For the copy-up operation, there two basic approaches. +- copy the specified XATTR only (by category above), and return the + error unconditionally if it happens. +- copy all XATTR, and ignore the error on the specified category only. + +In order to support XATTR and to implement the correct behaviour, aufs +chooses the latter approach and introduces some new branch attributes, +"icexsec", "icexsys", "icextr", "icexusr", and "icexoth". +They correspond to the XATTR namespaces (see above). Additionally, to be +convenient, "icex" is also provided which means all "icex*" attributes +are set (here the word "icex" stands for "ignore copy-error on XATTR"). + +The meaning of these attributes is to ignore the error from setting +XATTR on that branch. +Note that aufs tries copying all XATTR unconditionally, and ignores the +error from the dst branch according to the specified attributes. + +Some XATTR may have its default value. The default value may come from +the parent dir or the environment. If the default value is set at the +file creating-time, it will be overwritten by copy-up. +Some contradiction may happen I am afraid. +Do we need another attribute to stop copying XATTR? I am unsure. For +now, aufs implements the branch attributes to ignore the error. diff --git a/Documentation/filesystems/aufs/design/07export.txt b/Documentation/filesystems/aufs/design/07export.txt new file mode 100644 index 0000000000000..359151931fe2a --- /dev/null +++ b/Documentation/filesystems/aufs/design/07export.txt @@ -0,0 +1,58 @@ + +# Copyright (C) 2005-2021 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +Export Aufs via NFS +---------------------------------------------------------------------- +Here is an approach. +- like xino/xib, add a new file 'xigen' which stores aufs inode + generation. +- iget_locked(): initialize aufs inode generation for a new inode, and + store it in xigen file. +- destroy_inode(): increment aufs inode generation and store it in xigen + file. it is necessary even if it is not unlinked, because any data of + inode may be changed by UDBA. +- encode_fh(): for a root dir, simply return FILEID_ROOT. otherwise + build file handle by + + branch id (4 bytes) + + superblock generation (4 bytes) + + inode number (4 or 8 bytes) + + parent dir inode number (4 or 8 bytes) + + inode generation (4 bytes)) + + return value of exportfs_encode_fh() for the parent on a branch (4 + bytes) + + file handle for a branch (by exportfs_encode_fh()) +- fh_to_dentry(): + + find the index of a branch from its id in handle, and check it is + still exist in aufs. + + 1st level: get the inode number from handle and search it in cache. + + 2nd level: if not found in cache, get the parent inode number from + the handle and search it in cache. and then open the found parent + dir, find the matching inode number by vfs_readdir() and get its + name, and call lookup_one_len() for the target dentry. + + 3rd level: if the parent dir is not cached, call + exportfs_decode_fh() for a branch and get the parent on a branch, + build a pathname of it, convert it a pathname in aufs, call + path_lookup(). now aufs gets a parent dir dentry, then handle it as + the 2nd level. + + to open the dir, aufs needs struct vfsmount. aufs keeps vfsmount + for every branch, but not itself. to get this, (currently) aufs + searches in current->nsproxy->mnt_ns list. it may not be a good + idea, but I didn't get other approach. + + test the generation of the gotten inode. +- every inode operation: they may get EBUSY due to UDBA. in this case, + convert it into ESTALE for NFSD. +- readdir(): call lockdep_on/off() because filldir in NFSD calls + lookup_one_len(), vfs_getattr(), encode_fh() and others. diff --git a/Documentation/filesystems/aufs/design/08shwh.txt b/Documentation/filesystems/aufs/design/08shwh.txt new file mode 100644 index 0000000000000..a33e574246b23 --- /dev/null +++ b/Documentation/filesystems/aufs/design/08shwh.txt @@ -0,0 +1,52 @@ + +# Copyright (C) 2005-2021 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +Show Whiteout Mode (shwh) +---------------------------------------------------------------------- +Generally aufs hides the name of whiteouts. But in some cases, to show +them is very useful for users. For instance, creating a new middle layer +(branch) by merging existing layers. + +(borrowing aufs1 HOW-TO from a user, Michael Towers) +When you have three branches, +- Bottom: 'system', squashfs (underlying base system), read-only +- Middle: 'mods', squashfs, read-only +- Top: 'overlay', ram (tmpfs), read-write + +The top layer is loaded at boot time and saved at shutdown, to preserve +the changes made to the system during the session. +When larger changes have been made, or smaller changes have accumulated, +the size of the saved top layer data grows. At this point, it would be +nice to be able to merge the two overlay branches ('mods' and 'overlay') +and rewrite the 'mods' squashfs, clearing the top layer and thus +restoring save and load speed. + +This merging is simplified by the use of another aufs mount, of just the +two overlay branches using the 'shwh' option. +# mount -t aufs -o ro,shwh,br:/livesys/overlay=ro+wh:/livesys/mods=rr+wh \ + aufs /livesys/merge_union + +A merged view of these two branches is then available at +/livesys/merge_union, and the new feature is that the whiteouts are +visible! +Note that in 'shwh' mode the aufs mount must be 'ro', which will disable +writing to all branches. Also the default mode for all branches is 'ro'. +It is now possible to save the combined contents of the two overlay +branches to a new squashfs, e.g.: +# mksquashfs /livesys/merge_union /path/to/newmods.squash + +This new squashfs archive can be stored on the boot device and the +initramfs will use it to replace the old one at the next boot. diff --git a/Documentation/filesystems/aufs/design/10dynop.txt b/Documentation/filesystems/aufs/design/10dynop.txt new file mode 100644 index 0000000000000..0f1ce3ce90866 --- /dev/null +++ b/Documentation/filesystems/aufs/design/10dynop.txt @@ -0,0 +1,47 @@ + +# Copyright (C) 2010-2021 Junjiro R. Okajima +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +Dynamically customizable FS operations +---------------------------------------------------------------------- +Generally FS operations (struct inode_operations, struct +address_space_operations, struct file_operations, etc.) are defined as +"static const", but it never means that FS have only one set of +operation. Some FS have multiple sets of them. For instance, ext2 has +three sets, one for XIP, for NOBH, and for normal. +Since aufs overrides and redirects these operations, sometimes aufs has +to change its behaviour according to the branch FS type. More importantly +VFS acts differently if a function (member in the struct) is set or +not. It means aufs should have several sets of operations and select one +among them according to the branch FS definition. + +In order to solve this problem and not to affect the behaviour of VFS, +aufs defines these operations dynamically. For instance, aufs defines +dummy direct_IO function for struct address_space_operations, but it may +not be set to the address_space_operations actually. When the branch FS +doesn't have it, aufs doesn't set it to its address_space_operations +while the function definition itself is still alive. So the behaviour +itself will not change, and it will return an error when direct_IO is +not set. + +The lifetime of these dynamically generated operation object is +maintained by aufs branch object. When the branch is removed from aufs, +the reference counter of the object is decremented. When it reaches +zero, the dynamically generated operation object will be freed. + +This approach is designed to support AIO (io_submit), Direct I/O and +XIP (DAX) mainly. +Currently this approach is applied to address_space_operations for +regular files only. diff --git a/fs/aufs/Kconfig b/fs/aufs/Kconfig new file mode 100644 index 0000000000000..a5008b87a55f1 --- /dev/null +++ b/fs/aufs/Kconfig @@ -0,0 +1,199 @@ +# SPDX-License-Identifier: GPL-2.0 +config AUFS_FS + tristate "Aufs (Advanced multi layered unification filesystem) support" + help + Aufs is a stackable unification filesystem such as Unionfs, + which unifies several directories and provides a merged single + directory. + In the early days, aufs was entirely re-designed and + re-implemented Unionfs Version 1.x series. Introducing many + original ideas, approaches and improvements, it becomes totally + different from Unionfs while keeping the basic features. + +if AUFS_FS +choice + prompt "Maximum number of branches" + default AUFS_BRANCH_MAX_127 + help + Specifies the maximum number of branches (or member directories) + in a single aufs. The larger value consumes more system + resources and has a minor impact to performance. +config AUFS_BRANCH_MAX_127 + bool "127" + help + Specifies the maximum number of branches (or member directories) + in a single aufs. The larger value consumes more system + resources and has a minor impact to performance. +config AUFS_BRANCH_MAX_511 + bool "511" + help + Specifies the maximum number of branches (or member directories) + in a single aufs. The larger value consumes more system + resources and has a minor impact to performance. +config AUFS_BRANCH_MAX_1023 + bool "1023" + help + Specifies the maximum number of branches (or member directories) + in a single aufs. The larger value consumes more system + resources and has a minor impact to performance. +config AUFS_BRANCH_MAX_32767 + bool "32767" + help + Specifies the maximum number of branches (or member directories) + in a single aufs. The larger value consumes more system + resources and has a minor impact to performance. +endchoice + +config AUFS_SBILIST + bool + depends on AUFS_MAGIC_SYSRQ || PROC_FS + default y + help + Automatic configuration for internal use. + When aufs supports Magic SysRq or /proc, enabled automatically. + +config AUFS_HNOTIFY + bool "Detect direct branch access (bypassing aufs)" + help + If you want to modify files on branches directly, eg. bypassing aufs, + and want aufs to detect the changes of them fully, then enable this + option and use 'udba=notify' mount option. + Currently there is only one available configuration, "fsnotify". + It will have a negative impact to the performance. + See detail in aufs.5. + +choice + prompt "method" if AUFS_HNOTIFY + default AUFS_HFSNOTIFY +config AUFS_HFSNOTIFY + bool "fsnotify" + select FSNOTIFY +endchoice + +config AUFS_EXPORT + bool "NFS-exportable aufs" + depends on EXPORTFS + help + If you want to export your mounted aufs via NFS, then enable this + option. There are several requirements for this configuration. + See detail in aufs.5. + +config AUFS_INO_T_64 + bool + depends on AUFS_EXPORT + depends on 64BIT && !(ALPHA || S390) + default y + help + Automatic configuration for internal use. + /* typedef unsigned long/int __kernel_ino_t */ + /* alpha and s390x are int */ + +config AUFS_XATTR + bool "support for XATTR/EA (including Security Labels)" + help + If your branch fs supports XATTR/EA and you want to make them + available in aufs too, then enable this opsion and specify the + branch attributes for EA. + See detail in aufs.5. + +config AUFS_FHSM + bool "File-based Hierarchical Storage Management" + help + Hierarchical Storage Management (or HSM) is a well-known feature + in the storage world. Aufs provides this feature as file-based. + with multiple branches. + These multiple branches are prioritized, ie. the topmost one + should be the fastest drive and be used heavily. + +config AUFS_RDU + bool "Readdir in userspace" + help + Aufs has two methods to provide a merged view for a directory, + by a user-space library and by kernel-space natively. The latter + is always enabled but sometimes large and slow. + If you enable this option, install the library in aufs2-util + package, and set some environment variables for your readdir(3), + then the work will be handled in user-space which generally + shows better performance in most cases. + See detail in aufs.5. + +config AUFS_DIRREN + bool "Workaround for rename(2)-ing a directory" + help + By default, aufs returns EXDEV error in renameing a dir who has + his child on the lower branch, since it is a bad idea to issue + rename(2) internally for every lower branch. But user may not + accept this behaviour. So here is a workaround to allow such + rename(2) and store some extra information on the writable + branch. Obviously this costs high (and I don't like it). + To use this feature, you need to enable this configuration AND + to specify the mount option `dirren.' + See details in aufs.5 and the design documents. + +config AUFS_SHWH + bool "Show whiteouts" + help + If you want to make the whiteouts in aufs visible, then enable + this option and specify 'shwh' mount option. Although it may + sounds like philosophy or something, but in technically it + simply shows the name of whiteout with keeping its behaviour. + +config AUFS_BR_RAMFS + bool "Ramfs (initramfs/rootfs) as an aufs branch" + help + If you want to use ramfs as an aufs branch fs, then enable this + option. Generally tmpfs is recommended. + Aufs prohibited them to be a branch fs by default, because + initramfs becomes unusable after switch_root or something + generally. If you sets initramfs as an aufs branch and boot your + system by switch_root, you will meet a problem easily since the + files in initramfs may be inaccessible. + Unless you are going to use ramfs as an aufs branch fs without + switch_root or something, leave it N. + +config AUFS_BR_FUSE + bool "Fuse fs as an aufs branch" + depends on FUSE_FS + select AUFS_POLL + help + If you want to use fuse-based userspace filesystem as an aufs + branch fs, then enable this option. + It implements the internal poll(2) operation which is + implemented by fuse only (curretnly). + +config AUFS_POLL + bool + help + Automatic configuration for internal use. + +config AUFS_BR_HFSPLUS + bool "Hfsplus as an aufs branch" + depends on HFSPLUS_FS + default y + help + If you want to use hfsplus fs as an aufs branch fs, then enable + this option. This option introduces a small overhead at + copying-up a file on hfsplus. + +config AUFS_BDEV_LOOP + bool + depends on BLK_DEV_LOOP + default y + help + Automatic configuration for internal use. + Convert =[ym] into =y. + +config AUFS_DEBUG + bool "Debug aufs" + help + Enable this to compile aufs internal debug code. + It will have a negative impact to the performance. + +config AUFS_MAGIC_SYSRQ + bool + depends on AUFS_DEBUG && MAGIC_SYSRQ + default y + help + Automatic configuration for internal use. + When aufs supports Magic SysRq, enabled automatically. +endif diff --git a/fs/aufs/Makefile b/fs/aufs/Makefile new file mode 100644 index 0000000000000..4af8ecde3e3fa --- /dev/null +++ b/fs/aufs/Makefile @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: GPL-2.0 + +include ${src}/magic.mk +ifeq (${CONFIG_AUFS_FS},m) +include ${src}/conf.mk +endif +-include ${src}/priv_def.mk + +# cf. include/linux/kernel.h +# enable pr_debug +ccflags-y += -DDEBUG +# sparse requires the full pathname +ifdef M +ccflags-y += -include ${M}/../../include/uapi/linux/aufs_type.h +else +ccflags-y += -include ${srctree}/include/uapi/linux/aufs_type.h +endif + +obj-$(CONFIG_AUFS_FS) += aufs.o +aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o fsctx.o \ + wkq.o vfsub.o dcsub.o \ + cpup.o whout.o wbr_policy.o \ + dinfo.o dentry.o \ + dynop.o \ + finfo.o file.o f_op.o \ + dir.o vdir.o \ + iinfo.o inode.o i_op.o i_op_add.o i_op_del.o i_op_ren.o \ + mvdown.o ioctl.o + +# all are boolean +aufs-$(CONFIG_PROC_FS) += procfs.o plink.o +aufs-$(CONFIG_SYSFS) += sysfs.o +aufs-$(CONFIG_DEBUG_FS) += dbgaufs.o +aufs-$(CONFIG_AUFS_BDEV_LOOP) += loop.o +aufs-$(CONFIG_AUFS_HNOTIFY) += hnotify.o +aufs-$(CONFIG_AUFS_HFSNOTIFY) += hfsnotify.o +aufs-$(CONFIG_AUFS_EXPORT) += export.o +aufs-$(CONFIG_AUFS_XATTR) += xattr.o +aufs-$(CONFIG_FS_POSIX_ACL) += posix_acl.o +aufs-$(CONFIG_AUFS_DIRREN) += dirren.o +aufs-$(CONFIG_AUFS_FHSM) += fhsm.o +aufs-$(CONFIG_AUFS_POLL) += poll.o +aufs-$(CONFIG_AUFS_RDU) += rdu.o +aufs-$(CONFIG_AUFS_BR_HFSPLUS) += hfsplus.o +aufs-$(CONFIG_AUFS_DEBUG) += debug.o +aufs-$(CONFIG_AUFS_MAGIC_SYSRQ) += sysrq.o diff --git a/fs/aufs/aufs.h b/fs/aufs/aufs.h new file mode 100644 index 0000000000000..1ec7a347cba1e --- /dev/null +++ b/fs/aufs/aufs.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * all header files + */ + +#ifndef __AUFS_H__ +#define __AUFS_H__ + +#ifdef __KERNEL__ + +#define AuStub(type, name, body, ...) \ + static inline type name(__VA_ARGS__) { body; } + +#define AuStubVoid(name, ...) \ + AuStub(void, name, , __VA_ARGS__) +#define AuStubInt0(name, ...) \ + AuStub(int, name, return 0, __VA_ARGS__) + +#include "debug.h" + +#include "branch.h" +#include "cpup.h" +#include "dcsub.h" +#include "dbgaufs.h" +#include "dentry.h" +#include "dir.h" +#include "dirren.h" +#include "dynop.h" +#include "file.h" +#include "fstype.h" +#include "hbl.h" +#include "inode.h" +#include "lcnt.h" +#include "loop.h" +#include "module.h" +#include "opts.h" +#include "rwsem.h" +#include "super.h" +#include "sysaufs.h" +#include "vfsub.h" +#include "whout.h" +#include "wkq.h" + +#endif /* __KERNEL__ */ +#endif /* __AUFS_H__ */ diff --git a/fs/aufs/branch.c b/fs/aufs/branch.c new file mode 100644 index 0000000000000..e489a08076360 --- /dev/null +++ b/fs/aufs/branch.c @@ -0,0 +1,1427 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * branch management + */ + +#include +#include +#include "aufs.h" + +/* + * free a single branch + */ +static void au_br_do_free(struct au_branch *br) +{ + int i; + struct au_wbr *wbr; + struct au_dykey **key; + + au_hnotify_fin_br(br); + /* always, regardless the mount option */ + au_dr_hino_free(&br->br_dirren); + au_xino_put(br); + + AuLCntZero(au_lcnt_read(&br->br_nfiles, /*do_rev*/0)); + au_lcnt_fin(&br->br_nfiles, /*do_sync*/0); + AuLCntZero(au_lcnt_read(&br->br_count, /*do_rev*/0)); + au_lcnt_fin(&br->br_count, /*do_sync*/0); + + wbr = br->br_wbr; + if (wbr) { + for (i = 0; i < AuBrWh_Last; i++) + dput(wbr->wbr_wh[i]); + AuDebugOn(atomic_read(&wbr->wbr_wh_running)); + AuRwDestroy(&wbr->wbr_wh_rwsem); + } + + if (br->br_fhsm) { + au_br_fhsm_fin(br->br_fhsm); + au_kfree_try_rcu(br->br_fhsm); + } + + key = br->br_dykey; + for (i = 0; i < AuBrDynOp; i++, key++) + if (*key) + au_dy_put(*key); + else + break; + + /* recursive lock, s_umount of branch's */ + /* synchronize_rcu(); */ /* why? */ + lockdep_off(); + path_put(&br->br_path); + lockdep_on(); + au_kfree_rcu(wbr); + au_lcnt_wait_for_fin(&br->br_nfiles); + au_lcnt_wait_for_fin(&br->br_count); + /* I don't know why, but percpu_refcount requires this */ + /* synchronize_rcu(); */ + au_kfree_rcu(br); +} + +/* + * frees all branches + */ +void au_br_free(struct au_sbinfo *sbinfo) +{ + aufs_bindex_t bmax; + struct au_branch **br; + + AuRwMustWriteLock(&sbinfo->si_rwsem); + + bmax = sbinfo->si_bbot + 1; + br = sbinfo->si_branch; + while (bmax--) + au_br_do_free(*br++); +} + +/* + * find the index of a branch which is specified by @br_id. + */ +int au_br_index(struct super_block *sb, aufs_bindex_t br_id) +{ + aufs_bindex_t bindex, bbot; + + bbot = au_sbbot(sb); + for (bindex = 0; bindex <= bbot; bindex++) + if (au_sbr_id(sb, bindex) == br_id) + return bindex; + return -1; +} + +/* ---------------------------------------------------------------------- */ + +/* + * add a branch + */ + +static int test_overlap(struct super_block *sb, struct dentry *h_adding, + struct dentry *h_root) +{ + if (unlikely(h_adding == h_root + || au_test_loopback_overlap(sb, h_adding))) + return 1; + if (h_adding->d_sb != h_root->d_sb) + return 0; + return au_test_subdir(h_adding, h_root) + || au_test_subdir(h_root, h_adding); +} + +/* + * returns a newly allocated branch. @new_nbranch is a number of branches + * after adding a branch. + */ +static struct au_branch *au_br_alloc(struct super_block *sb, int new_nbranch, + int perm) +{ + struct au_branch *add_branch; + struct dentry *root; + struct inode *inode; + int err; + + err = -ENOMEM; + add_branch = kzalloc(sizeof(*add_branch), GFP_NOFS); + if (unlikely(!add_branch)) + goto out; + add_branch->br_xino = au_xino_alloc(/*nfile*/1); + if (unlikely(!add_branch->br_xino)) + goto out_br; + err = au_hnotify_init_br(add_branch, perm); + if (unlikely(err)) + goto out_xino; + + if (au_br_writable(perm)) { + /* may be freed separately at changing the branch permission */ + add_branch->br_wbr = kzalloc(sizeof(*add_branch->br_wbr), + GFP_NOFS); + if (unlikely(!add_branch->br_wbr)) + goto out_hnotify; + } + + if (au_br_fhsm(perm)) { + err = au_fhsm_br_alloc(add_branch); + if (unlikely(err)) + goto out_wbr; + } + + root = sb->s_root; + err = au_sbr_realloc(au_sbi(sb), new_nbranch, /*may_shrink*/0); + if (!err) + err = au_di_realloc(au_di(root), new_nbranch, /*may_shrink*/0); + if (!err) { + inode = d_inode(root); + err = au_hinode_realloc(au_ii(inode), new_nbranch, + /*may_shrink*/0); + } + if (!err) + return add_branch; /* success */ + +out_wbr: + au_kfree_rcu(add_branch->br_wbr); +out_hnotify: + au_hnotify_fin_br(add_branch); +out_xino: + au_xino_put(add_branch); +out_br: + au_kfree_rcu(add_branch); +out: + return ERR_PTR(err); +} + +/* + * test if the branch permission is legal or not. + */ +static int test_br(struct inode *inode, int brperm, char *path) +{ + int err; + + err = (au_br_writable(brperm) && IS_RDONLY(inode)); + if (!err) + goto out; + + err = -EINVAL; + pr_err("write permission for readonly mount or inode, %s\n", path); + +out: + return err; +} + +/* + * returns: + * 0: success, the caller will add it + * plus: success, it is already unified, the caller should ignore it + * minus: error + */ +static int test_add(struct super_block *sb, struct au_opt_add *add, int remount) +{ + int err; + aufs_bindex_t bbot, bindex; + struct dentry *root, *h_dentry; + struct inode *inode, *h_inode; + + root = sb->s_root; + bbot = au_sbbot(sb); + if (unlikely(bbot >= 0 + && au_find_dbindex(root, add->path.dentry) >= 0)) { + err = 1; + if (!remount) { + err = -EINVAL; + pr_err("%s duplicated\n", add->pathname); + } + goto out; + } + + err = -ENOSPC; /* -E2BIG; */ + if (unlikely(AUFS_BRANCH_MAX <= add->bindex + || AUFS_BRANCH_MAX - 1 <= bbot)) { + pr_err("number of branches exceeded %s\n", add->pathname); + goto out; + } + + err = -EDOM; + if (unlikely(add->bindex < 0 || bbot + 1 < add->bindex)) { + pr_err("bad index %d\n", add->bindex); + goto out; + } + + inode = d_inode(add->path.dentry); + err = -ENOENT; + if (unlikely(!inode->i_nlink)) { + pr_err("no existence %s\n", add->pathname); + goto out; + } + + err = -EINVAL; + if (unlikely(inode->i_sb == sb)) { + pr_err("%s must be outside\n", add->pathname); + goto out; + } + + if (unlikely(au_test_fs_unsuppoted(inode->i_sb))) { + pr_err("unsupported filesystem, %s (%s)\n", + add->pathname, au_sbtype(inode->i_sb)); + goto out; + } + + if (unlikely(inode->i_sb->s_stack_depth)) { + pr_err("already stacked, %s (%s)\n", + add->pathname, au_sbtype(inode->i_sb)); + goto out; + } + + err = test_br(d_inode(add->path.dentry), add->perm, add->pathname); + if (unlikely(err)) + goto out; + + if (bbot < 0) + return 0; /* success */ + + err = -EINVAL; + for (bindex = 0; bindex <= bbot; bindex++) + if (unlikely(test_overlap(sb, add->path.dentry, + au_h_dptr(root, bindex)))) { + pr_err("%s is overlapped\n", add->pathname); + goto out; + } + + err = 0; + if (au_opt_test(au_mntflags(sb), WARN_PERM)) { + h_dentry = au_h_dptr(root, 0); + h_inode = d_inode(h_dentry); + if ((h_inode->i_mode & S_IALLUGO) != (inode->i_mode & S_IALLUGO) + || !uid_eq(h_inode->i_uid, inode->i_uid) + || !gid_eq(h_inode->i_gid, inode->i_gid)) + pr_warn("uid/gid/perm %s %u/%u/0%o, %u/%u/0%o\n", + add->pathname, + i_uid_read(inode), i_gid_read(inode), + (inode->i_mode & S_IALLUGO), + i_uid_read(h_inode), i_gid_read(h_inode), + (h_inode->i_mode & S_IALLUGO)); + } + +out: + return err; +} + +/* + * initialize or clean the whiteouts for an adding branch + */ +static int au_br_init_wh(struct super_block *sb, struct au_branch *br, + int new_perm) +{ + int err, old_perm; + aufs_bindex_t bindex; + struct inode *h_inode; + struct au_wbr *wbr; + struct au_hinode *hdir; + struct dentry *h_dentry; + + err = vfsub_mnt_want_write(au_br_mnt(br)); + if (unlikely(err)) + goto out; + + wbr = br->br_wbr; + old_perm = br->br_perm; + br->br_perm = new_perm; + hdir = NULL; + h_inode = NULL; + bindex = au_br_index(sb, br->br_id); + if (0 <= bindex) { + hdir = au_hi(d_inode(sb->s_root), bindex); + au_hn_inode_lock_nested(hdir, AuLsc_I_PARENT); + } else { + h_dentry = au_br_dentry(br); + h_inode = d_inode(h_dentry); + inode_lock_nested(h_inode, AuLsc_I_PARENT); + } + if (!wbr) + err = au_wh_init(br, sb); + else { + wbr_wh_write_lock(wbr); + err = au_wh_init(br, sb); + wbr_wh_write_unlock(wbr); + } + if (hdir) + au_hn_inode_unlock(hdir); + else + inode_unlock(h_inode); + vfsub_mnt_drop_write(au_br_mnt(br)); + br->br_perm = old_perm; + + if (!err && wbr && !au_br_writable(new_perm)) { + au_kfree_rcu(wbr); + br->br_wbr = NULL; + } + +out: + return err; +} + +static int au_wbr_init(struct au_branch *br, struct super_block *sb, + int perm) +{ + int err; + struct kstatfs kst; + struct au_wbr *wbr; + + wbr = br->br_wbr; + au_rw_init(&wbr->wbr_wh_rwsem); + atomic_set(&wbr->wbr_wh_running, 0); + + /* + * a limit for rmdir/rename a dir + * cf. AUFS_MAX_NAMELEN in include/uapi/linux/aufs_type.h + */ + err = vfs_statfs(&br->br_path, &kst); + if (unlikely(err)) + goto out; + err = -EINVAL; + if (kst.f_namelen >= NAME_MAX) + err = au_br_init_wh(sb, br, perm); + else + pr_err("%pd(%s), unsupported namelen %ld\n", + au_br_dentry(br), + au_sbtype(au_br_dentry(br)->d_sb), kst.f_namelen); + +out: + return err; +} + +/* initialize a new branch */ +static int au_br_init(struct au_branch *br, struct super_block *sb, + struct au_opt_add *add) +{ + int err; + struct au_branch *brbase; + struct file *xf; + struct inode *h_inode; + + err = 0; + br->br_perm = add->perm; + br->br_path = add->path; /* set first, path_get() later */ + spin_lock_init(&br->br_dykey_lock); + au_lcnt_init(&br->br_nfiles, /*release*/NULL); + au_lcnt_init(&br->br_count, /*release*/NULL); + br->br_id = au_new_br_id(sb); + AuDebugOn(br->br_id < 0); + + /* always, regardless the given option */ + err = au_dr_br_init(sb, br, &add->path); + if (unlikely(err)) + goto out_err; + + if (au_br_writable(add->perm)) { + err = au_wbr_init(br, sb, add->perm); + if (unlikely(err)) + goto out_err; + } + + if (au_opt_test(au_mntflags(sb), XINO)) { + brbase = au_sbr(sb, 0); + xf = au_xino_file(brbase->br_xino, /*idx*/-1); + AuDebugOn(!xf); + h_inode = d_inode(add->path.dentry); + err = au_xino_init_br(sb, br, h_inode->i_ino, &xf->f_path); + if (unlikely(err)) { + AuDebugOn(au_xino_file(br->br_xino, /*idx*/-1)); + goto out_err; + } + } + + sysaufs_br_init(br); + path_get(&br->br_path); + goto out; /* success */ + +out_err: + memset(&br->br_path, 0, sizeof(br->br_path)); +out: + return err; +} + +static void au_br_do_add_brp(struct au_sbinfo *sbinfo, aufs_bindex_t bindex, + struct au_branch *br, aufs_bindex_t bbot, + aufs_bindex_t amount) +{ + struct au_branch **brp; + + AuRwMustWriteLock(&sbinfo->si_rwsem); + + brp = sbinfo->si_branch + bindex; + memmove(brp + 1, brp, sizeof(*brp) * amount); + *brp = br; + sbinfo->si_bbot++; + if (unlikely(bbot < 0)) + sbinfo->si_bbot = 0; +} + +static void au_br_do_add_hdp(struct au_dinfo *dinfo, aufs_bindex_t bindex, + aufs_bindex_t bbot, aufs_bindex_t amount) +{ + struct au_hdentry *hdp; + + AuRwMustWriteLock(&dinfo->di_rwsem); + + hdp = au_hdentry(dinfo, bindex); + memmove(hdp + 1, hdp, sizeof(*hdp) * amount); + au_h_dentry_init(hdp); + dinfo->di_bbot++; + if (unlikely(bbot < 0)) + dinfo->di_btop = 0; +} + +static void au_br_do_add_hip(struct au_iinfo *iinfo, aufs_bindex_t bindex, + aufs_bindex_t bbot, aufs_bindex_t amount) +{ + struct au_hinode *hip; + + AuRwMustWriteLock(&iinfo->ii_rwsem); + + hip = au_hinode(iinfo, bindex); + memmove(hip + 1, hip, sizeof(*hip) * amount); + au_hinode_init(hip); + iinfo->ii_bbot++; + if (unlikely(bbot < 0)) + iinfo->ii_btop = 0; +} + +static void au_br_do_add(struct super_block *sb, struct au_branch *br, + aufs_bindex_t bindex) +{ + struct dentry *root, *h_dentry; + struct inode *root_inode, *h_inode; + aufs_bindex_t bbot, amount; + + root = sb->s_root; + root_inode = d_inode(root); + bbot = au_sbbot(sb); + amount = bbot + 1 - bindex; + h_dentry = au_br_dentry(br); + au_sbilist_lock(); + au_br_do_add_brp(au_sbi(sb), bindex, br, bbot, amount); + au_br_do_add_hdp(au_di(root), bindex, bbot, amount); + au_br_do_add_hip(au_ii(root_inode), bindex, bbot, amount); + au_set_h_dptr(root, bindex, dget(h_dentry)); + h_inode = d_inode(h_dentry); + au_set_h_iptr(root_inode, bindex, au_igrab(h_inode), /*flags*/0); + au_sbilist_unlock(); +} + +int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount) +{ + int err; + aufs_bindex_t bbot, add_bindex; + struct dentry *root, *h_dentry; + struct inode *root_inode; + struct au_branch *add_branch; + + root = sb->s_root; + root_inode = d_inode(root); + IMustLock(root_inode); + IiMustWriteLock(root_inode); + err = test_add(sb, add, remount); + if (unlikely(err < 0)) + goto out; + if (err) { + err = 0; + goto out; /* success */ + } + + bbot = au_sbbot(sb); + add_branch = au_br_alloc(sb, bbot + 2, add->perm); + err = PTR_ERR(add_branch); + if (IS_ERR(add_branch)) + goto out; + + err = au_br_init(add_branch, sb, add); + if (unlikely(err)) { + au_br_do_free(add_branch); + goto out; + } + + add_bindex = add->bindex; + sysaufs_brs_del(sb, add_bindex); /* remove successors */ + au_br_do_add(sb, add_branch, add_bindex); + sysaufs_brs_add(sb, add_bindex); /* append successors */ + dbgaufs_brs_add(sb, add_bindex, /*topdown*/0); /* rename successors */ + + h_dentry = add->path.dentry; + if (!add_bindex) { + au_cpup_attr_all(root_inode, /*force*/1); + sb->s_maxbytes = h_dentry->d_sb->s_maxbytes; + } else + au_add_nlink(root_inode, d_inode(h_dentry)); + +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +static unsigned long long au_farray_cb(struct super_block *sb, void *a, + unsigned long long max __maybe_unused, + void *arg) +{ + unsigned long long n; + struct file **p, *f; + struct hlist_bl_head *files; + struct hlist_bl_node *pos; + struct au_finfo *finfo; + + n = 0; + p = a; + files = &au_sbi(sb)->si_files; + hlist_bl_lock(files); + hlist_bl_for_each_entry(finfo, pos, files, fi_hlist) { + f = finfo->fi_file; + if (file_count(f) + && !special_file(file_inode(f)->i_mode)) { + get_file(f); + *p++ = f; + n++; + AuDebugOn(n > max); + } + } + hlist_bl_unlock(files); + + return n; +} + +static struct file **au_farray_alloc(struct super_block *sb, + unsigned long long *max) +{ + struct au_sbinfo *sbi; + + sbi = au_sbi(sb); + *max = au_lcnt_read(&sbi->si_nfiles, /*do_rev*/1); + return au_array_alloc(max, au_farray_cb, sb, /*arg*/NULL); +} + +static void au_farray_free(struct file **a, unsigned long long max) +{ + unsigned long long ull; + + for (ull = 0; ull < max; ull++) + if (a[ull]) + fput(a[ull]); + kvfree(a); +} + +/* ---------------------------------------------------------------------- */ + +/* + * delete a branch + */ + +/* to show the line number, do not make it inlined function */ +#define AuVerbose(do_info, fmt, ...) do { \ + if (do_info) \ + pr_info(fmt, ##__VA_ARGS__); \ +} while (0) + +static int au_test_ibusy(struct inode *inode, aufs_bindex_t btop, + aufs_bindex_t bbot) +{ + return (inode && !S_ISDIR(inode->i_mode)) || btop == bbot; +} + +static int au_test_dbusy(struct dentry *dentry, aufs_bindex_t btop, + aufs_bindex_t bbot) +{ + return au_test_ibusy(d_inode(dentry), btop, bbot); +} + +/* + * test if the branch is deletable or not. + */ +static int test_dentry_busy(struct dentry *root, aufs_bindex_t bindex, + unsigned int sigen, const unsigned int verbose) +{ + int err, i, j, ndentry; + aufs_bindex_t btop, bbot; + struct au_dcsub_pages dpages; + struct au_dpage *dpage; + struct dentry *d; + + err = au_dpages_init(&dpages, GFP_NOFS); + if (unlikely(err)) + goto out; + err = au_dcsub_pages(&dpages, root, NULL, NULL); + if (unlikely(err)) + goto out_dpages; + + for (i = 0; !err && i < dpages.ndpage; i++) { + dpage = dpages.dpages + i; + ndentry = dpage->ndentry; + for (j = 0; !err && j < ndentry; j++) { + d = dpage->dentries[j]; + AuDebugOn(au_dcount(d) <= 0); + if (!au_digen_test(d, sigen)) { + di_read_lock_child(d, AuLock_IR); + if (unlikely(au_dbrange_test(d))) { + di_read_unlock(d, AuLock_IR); + continue; + } + } else { + di_write_lock_child(d); + if (unlikely(au_dbrange_test(d))) { + di_write_unlock(d); + continue; + } + err = au_reval_dpath(d, sigen); + if (!err) + di_downgrade_lock(d, AuLock_IR); + else { + di_write_unlock(d); + break; + } + } + + /* AuDbgDentry(d); */ + btop = au_dbtop(d); + bbot = au_dbbot(d); + if (btop <= bindex + && bindex <= bbot + && au_h_dptr(d, bindex) + && au_test_dbusy(d, btop, bbot)) { + err = -EBUSY; + AuVerbose(verbose, "busy %pd\n", d); + AuDbgDentry(d); + } + di_read_unlock(d, AuLock_IR); + } + } + +out_dpages: + au_dpages_free(&dpages); +out: + return err; +} + +static int test_inode_busy(struct super_block *sb, aufs_bindex_t bindex, + unsigned int sigen, const unsigned int verbose) +{ + int err; + unsigned long long max, ull; + struct inode *i, **array; + aufs_bindex_t btop, bbot; + + array = au_iarray_alloc(sb, &max); + err = PTR_ERR(array); + if (IS_ERR(array)) + goto out; + + err = 0; + AuDbg("b%d\n", bindex); + for (ull = 0; !err && ull < max; ull++) { + i = array[ull]; + if (unlikely(!i)) + break; + if (i->i_ino == AUFS_ROOT_INO) + continue; + + /* AuDbgInode(i); */ + if (au_iigen(i, NULL) == sigen) + ii_read_lock_child(i); + else { + ii_write_lock_child(i); + err = au_refresh_hinode_self(i); + au_iigen_dec(i); + if (!err) + ii_downgrade_lock(i); + else { + ii_write_unlock(i); + break; + } + } + + btop = au_ibtop(i); + bbot = au_ibbot(i); + if (btop <= bindex + && bindex <= bbot + && au_h_iptr(i, bindex) + && au_test_ibusy(i, btop, bbot)) { + err = -EBUSY; + AuVerbose(verbose, "busy i%lu\n", i->i_ino); + AuDbgInode(i); + } + ii_read_unlock(i); + } + au_iarray_free(array, max); + +out: + return err; +} + +static int test_children_busy(struct dentry *root, aufs_bindex_t bindex, + const unsigned int verbose) +{ + int err; + unsigned int sigen; + + sigen = au_sigen(root->d_sb); + DiMustNoWaiters(root); + IiMustNoWaiters(d_inode(root)); + di_write_unlock(root); + err = test_dentry_busy(root, bindex, sigen, verbose); + if (!err) + err = test_inode_busy(root->d_sb, bindex, sigen, verbose); + di_write_lock_child(root); /* aufs_write_lock() calls ..._child() */ + + return err; +} + +static int test_dir_busy(struct file *file, aufs_bindex_t br_id, + struct file **to_free, int *idx) +{ + int err; + unsigned char matched, root; + aufs_bindex_t bindex, bbot; + struct au_fidir *fidir; + struct au_hfile *hfile; + + err = 0; + root = IS_ROOT(file->f_path.dentry); + if (root) { + get_file(file); + to_free[*idx] = file; + (*idx)++; + goto out; + } + + matched = 0; + fidir = au_fi(file)->fi_hdir; + AuDebugOn(!fidir); + bbot = au_fbbot_dir(file); + for (bindex = au_fbtop(file); bindex <= bbot; bindex++) { + hfile = fidir->fd_hfile + bindex; + if (!hfile->hf_file) + continue; + + if (hfile->hf_br->br_id == br_id) { + matched = 1; + break; + } + } + if (matched) + err = -EBUSY; + +out: + return err; +} + +static int test_file_busy(struct super_block *sb, aufs_bindex_t br_id, + struct file **to_free, int opened) +{ + int err, idx; + unsigned long long ull, max; + aufs_bindex_t btop; + struct file *file, **array; + struct dentry *root; + struct au_hfile *hfile; + + array = au_farray_alloc(sb, &max); + err = PTR_ERR(array); + if (IS_ERR(array)) + goto out; + + err = 0; + idx = 0; + root = sb->s_root; + di_write_unlock(root); + for (ull = 0; ull < max; ull++) { + file = array[ull]; + if (unlikely(!file)) + break; + + /* AuDbg("%pD\n", file); */ + fi_read_lock(file); + btop = au_fbtop(file); + if (!d_is_dir(file->f_path.dentry)) { + hfile = &au_fi(file)->fi_htop; + if (hfile->hf_br->br_id == br_id) + err = -EBUSY; + } else + err = test_dir_busy(file, br_id, to_free, &idx); + fi_read_unlock(file); + if (unlikely(err)) + break; + } + di_write_lock_child(root); + au_farray_free(array, max); + AuDebugOn(idx > opened); + +out: + return err; +} + +static void br_del_file(struct file **to_free, unsigned long long opened, + aufs_bindex_t br_id) +{ + unsigned long long ull; + aufs_bindex_t bindex, btop, bbot, bfound; + struct file *file; + struct au_fidir *fidir; + struct au_hfile *hfile; + + for (ull = 0; ull < opened; ull++) { + file = to_free[ull]; + if (unlikely(!file)) + break; + + /* AuDbg("%pD\n", file); */ + AuDebugOn(!d_is_dir(file->f_path.dentry)); + bfound = -1; + fidir = au_fi(file)->fi_hdir; + AuDebugOn(!fidir); + fi_write_lock(file); + btop = au_fbtop(file); + bbot = au_fbbot_dir(file); + for (bindex = btop; bindex <= bbot; bindex++) { + hfile = fidir->fd_hfile + bindex; + if (!hfile->hf_file) + continue; + + if (hfile->hf_br->br_id == br_id) { + bfound = bindex; + break; + } + } + AuDebugOn(bfound < 0); + au_set_h_fptr(file, bfound, NULL); + if (bfound == btop) { + for (btop++; btop <= bbot; btop++) + if (au_hf_dir(file, btop)) { + au_set_fbtop(file, btop); + break; + } + } + fi_write_unlock(file); + } +} + +static void au_br_do_del_brp(struct au_sbinfo *sbinfo, + const aufs_bindex_t bindex, + const aufs_bindex_t bbot) +{ + struct au_branch **brp, **p; + + AuRwMustWriteLock(&sbinfo->si_rwsem); + + brp = sbinfo->si_branch + bindex; + if (bindex < bbot) + memmove(brp, brp + 1, sizeof(*brp) * (bbot - bindex)); + sbinfo->si_branch[0 + bbot] = NULL; + sbinfo->si_bbot--; + + p = au_krealloc(sbinfo->si_branch, sizeof(*p) * bbot, AuGFP_SBILIST, + /*may_shrink*/1); + if (p) + sbinfo->si_branch = p; + /* harmless error */ +} + +static void au_br_do_del_hdp(struct au_dinfo *dinfo, const aufs_bindex_t bindex, + const aufs_bindex_t bbot) +{ + struct au_hdentry *hdp, *p; + + AuRwMustWriteLock(&dinfo->di_rwsem); + + hdp = au_hdentry(dinfo, bindex); + if (bindex < bbot) + memmove(hdp, hdp + 1, sizeof(*hdp) * (bbot - bindex)); + /* au_h_dentry_init(au_hdentry(dinfo, bbot); */ + dinfo->di_bbot--; + + p = au_krealloc(dinfo->di_hdentry, sizeof(*p) * bbot, AuGFP_SBILIST, + /*may_shrink*/1); + if (p) + dinfo->di_hdentry = p; + /* harmless error */ +} + +static void au_br_do_del_hip(struct au_iinfo *iinfo, const aufs_bindex_t bindex, + const aufs_bindex_t bbot) +{ + struct au_hinode *hip, *p; + + AuRwMustWriteLock(&iinfo->ii_rwsem); + + hip = au_hinode(iinfo, bindex); + if (bindex < bbot) + memmove(hip, hip + 1, sizeof(*hip) * (bbot - bindex)); + /* au_hinode_init(au_hinode(iinfo, bbot)); */ + iinfo->ii_bbot--; + + p = au_krealloc(iinfo->ii_hinode, sizeof(*p) * bbot, AuGFP_SBILIST, + /*may_shrink*/1); + if (p) + iinfo->ii_hinode = p; + /* harmless error */ +} + +static void au_br_do_del(struct super_block *sb, aufs_bindex_t bindex, + struct au_branch *br) +{ + aufs_bindex_t bbot; + struct au_sbinfo *sbinfo; + struct dentry *root, *h_root; + struct inode *inode, *h_inode; + struct au_hinode *hinode; + + SiMustWriteLock(sb); + + root = sb->s_root; + inode = d_inode(root); + sbinfo = au_sbi(sb); + bbot = sbinfo->si_bbot; + + h_root = au_h_dptr(root, bindex); + hinode = au_hi(inode, bindex); + h_inode = au_igrab(hinode->hi_inode); + au_hiput(hinode); + + au_sbilist_lock(); + au_br_do_del_brp(sbinfo, bindex, bbot); + au_br_do_del_hdp(au_di(root), bindex, bbot); + au_br_do_del_hip(au_ii(inode), bindex, bbot); + au_sbilist_unlock(); + + /* ignore an error */ + au_dr_br_fin(sb, br); /* always, regardless the mount option */ + + dput(h_root); + iput(h_inode); + au_br_do_free(br); +} + +static unsigned long long empty_cb(struct super_block *sb, void *array, + unsigned long long max, void *arg) +{ + return max; +} + +int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount) +{ + int err, rerr, i; + unsigned long long opened; + unsigned int mnt_flags; + aufs_bindex_t bindex, bbot, br_id; + unsigned char do_wh, verbose; + struct au_branch *br; + struct au_wbr *wbr; + struct dentry *root; + struct file **to_free; + + err = 0; + opened = 0; + to_free = NULL; + root = sb->s_root; + bindex = au_find_dbindex(root, del->h_path.dentry); + if (bindex < 0) { + if (remount) + goto out; /* success */ + err = -ENOENT; + pr_err("%s no such branch\n", del->pathname); + goto out; + } + AuDbg("bindex b%d\n", bindex); + + err = -EBUSY; + mnt_flags = au_mntflags(sb); + verbose = !!au_opt_test(mnt_flags, VERBOSE); + bbot = au_sbbot(sb); + if (unlikely(!bbot)) { + AuVerbose(verbose, "no more branches left\n"); + goto out; + } + + br = au_sbr(sb, bindex); + AuDebugOn(!path_equal(&br->br_path, &del->h_path)); + if (unlikely(au_lcnt_read(&br->br_count, /*do_rev*/1))) { + AuVerbose(verbose, "br %pd2 is busy now\n", del->h_path.dentry); + goto out; + } + + br_id = br->br_id; + opened = au_lcnt_read(&br->br_nfiles, /*do_rev*/1); + if (unlikely(opened)) { + to_free = au_array_alloc(&opened, empty_cb, sb, NULL); + err = PTR_ERR(to_free); + if (IS_ERR(to_free)) + goto out; + + err = test_file_busy(sb, br_id, to_free, opened); + if (unlikely(err)) { + AuVerbose(verbose, "%llu file(s) opened\n", opened); + goto out; + } + } + + wbr = br->br_wbr; + do_wh = wbr && (wbr->wbr_whbase || wbr->wbr_plink || wbr->wbr_orph); + if (do_wh) { + /* instead of WbrWhMustWriteLock(wbr) */ + SiMustWriteLock(sb); + for (i = 0; i < AuBrWh_Last; i++) { + dput(wbr->wbr_wh[i]); + wbr->wbr_wh[i] = NULL; + } + } + + err = test_children_busy(root, bindex, verbose); + if (unlikely(err)) { + if (do_wh) + goto out_wh; + goto out; + } + + err = 0; + if (to_free) { + /* + * now we confirmed the branch is deletable. + * let's free the remaining opened dirs on the branch. + */ + di_write_unlock(root); + br_del_file(to_free, opened, br_id); + di_write_lock_child(root); + } + + sysaufs_brs_del(sb, bindex); /* remove successors */ + dbgaufs_xino_del(br); /* remove one */ + au_br_do_del(sb, bindex, br); + sysaufs_brs_add(sb, bindex); /* append successors */ + dbgaufs_brs_add(sb, bindex, /*topdown*/1); /* rename successors */ + + if (!bindex) { + au_cpup_attr_all(d_inode(root), /*force*/1); + sb->s_maxbytes = au_sbr_sb(sb, 0)->s_maxbytes; + } else + au_sub_nlink(d_inode(root), d_inode(del->h_path.dentry)); + if (au_opt_test(mnt_flags, PLINK)) + au_plink_half_refresh(sb, br_id); + + goto out; /* success */ + +out_wh: + /* revert */ + rerr = au_br_init_wh(sb, br, br->br_perm); + if (rerr) + pr_warn("failed re-creating base whiteout, %s. (%d)\n", + del->pathname, rerr); +out: + if (to_free) + au_farray_free(to_free, opened); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int au_ibusy(struct super_block *sb, struct aufs_ibusy __user *arg) +{ + int err; + aufs_bindex_t btop, bbot; + struct aufs_ibusy ibusy; + struct inode *inode, *h_inode; + + err = -EPERM; + if (unlikely(!capable(CAP_SYS_ADMIN))) + goto out; + + err = copy_from_user(&ibusy, arg, sizeof(ibusy)); + if (!err) + /* VERIFY_WRITE */ + err = !access_ok(&arg->h_ino, sizeof(arg->h_ino)); + if (unlikely(err)) { + err = -EFAULT; + AuTraceErr(err); + goto out; + } + + err = -EINVAL; + si_read_lock(sb, AuLock_FLUSH); + if (unlikely(ibusy.bindex < 0 || ibusy.bindex > au_sbbot(sb))) + goto out_unlock; + + err = 0; + ibusy.h_ino = 0; /* invalid */ + inode = ilookup(sb, ibusy.ino); + if (!inode + || inode->i_ino == AUFS_ROOT_INO + || au_is_bad_inode(inode)) + goto out_unlock; + + ii_read_lock_child(inode); + btop = au_ibtop(inode); + bbot = au_ibbot(inode); + if (btop <= ibusy.bindex && ibusy.bindex <= bbot) { + h_inode = au_h_iptr(inode, ibusy.bindex); + if (h_inode && au_test_ibusy(inode, btop, bbot)) + ibusy.h_ino = h_inode->i_ino; + } + ii_read_unlock(inode); + iput(inode); + +out_unlock: + si_read_unlock(sb); + if (!err) { + err = __put_user(ibusy.h_ino, &arg->h_ino); + if (unlikely(err)) { + err = -EFAULT; + AuTraceErr(err); + } + } +out: + return err; +} + +long au_ibusy_ioctl(struct file *file, unsigned long arg) +{ + return au_ibusy(file->f_path.dentry->d_sb, (void __user *)arg); +} + +#ifdef CONFIG_COMPAT +long au_ibusy_compat_ioctl(struct file *file, unsigned long arg) +{ + return au_ibusy(file->f_path.dentry->d_sb, compat_ptr(arg)); +} +#endif + +/* ---------------------------------------------------------------------- */ + +/* + * change a branch permission + */ + +static void au_warn_ima(void) +{ +#ifdef CONFIG_IMA + /* since it doesn't support mark_files_ro() */ + AuWarn1("RW -> RO makes IMA to produce wrong message\n"); +#endif +} + +static int do_need_sigen_inc(int a, int b) +{ + return au_br_whable(a) && !au_br_whable(b); +} + +static int need_sigen_inc(int old, int new) +{ + return do_need_sigen_inc(old, new) + || do_need_sigen_inc(new, old); +} + +static int au_br_mod_files_ro(struct super_block *sb, aufs_bindex_t bindex) +{ + int err, do_warn; + unsigned int mnt_flags; + unsigned long long ull, max; + aufs_bindex_t br_id; + unsigned char verbose, writer; + struct file *file, *hf, **array; + struct au_hfile *hfile; + struct inode *h_inode; + + mnt_flags = au_mntflags(sb); + verbose = !!au_opt_test(mnt_flags, VERBOSE); + + array = au_farray_alloc(sb, &max); + err = PTR_ERR(array); + if (IS_ERR(array)) + goto out; + + do_warn = 0; + br_id = au_sbr_id(sb, bindex); + for (ull = 0; ull < max; ull++) { + file = array[ull]; + if (unlikely(!file)) + break; + + /* AuDbg("%pD\n", file); */ + fi_read_lock(file); + if (unlikely(au_test_mmapped(file))) { + err = -EBUSY; + AuVerbose(verbose, "mmapped %pD\n", file); + AuDbgFile(file); + FiMustNoWaiters(file); + fi_read_unlock(file); + goto out_array; + } + + hfile = &au_fi(file)->fi_htop; + hf = hfile->hf_file; + if (!d_is_reg(file->f_path.dentry) + || !(file->f_mode & FMODE_WRITE) + || hfile->hf_br->br_id != br_id + || !(hf->f_mode & FMODE_WRITE)) + array[ull] = NULL; + else { + do_warn = 1; + get_file(file); + } + + FiMustNoWaiters(file); + fi_read_unlock(file); + fput(file); + } + + err = 0; + if (do_warn) + au_warn_ima(); + + for (ull = 0; ull < max; ull++) { + file = array[ull]; + if (!file) + continue; + + /* todo: already flushed? */ + /* + * fs/super.c:mark_files_ro() is gone, but aufs keeps its + * approach which resets f_mode and calls mnt_drop_write() and + * file_release_write() for each file, because the branch + * attribute in aufs world is totally different from the native + * fs rw/ro mode. + */ + /* fi_read_lock(file); */ + hfile = &au_fi(file)->fi_htop; + hf = hfile->hf_file; + /* fi_read_unlock(file); */ + spin_lock(&hf->f_lock); + writer = !!(hf->f_mode & FMODE_WRITER); + hf->f_mode &= ~(FMODE_WRITE | FMODE_WRITER); + spin_unlock(&hf->f_lock); + if (writer) { + h_inode = file_inode(hf); + if (hf->f_mode & FMODE_READ) + i_readcount_inc(h_inode); + put_write_access(h_inode); + __mnt_drop_write(hf->f_path.mnt); + } + } + +out_array: + au_farray_free(array, max); +out: + AuTraceErr(err); + return err; +} + +int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount, + int *do_refresh) +{ + int err, rerr; + aufs_bindex_t bindex; + struct dentry *root; + struct au_branch *br; + struct au_br_fhsm *bf; + + root = sb->s_root; + bindex = au_find_dbindex(root, mod->h_root); + if (bindex < 0) { + if (remount) + return 0; /* success */ + err = -ENOENT; + pr_err("%s no such branch\n", mod->path); + goto out; + } + AuDbg("bindex b%d\n", bindex); + + err = test_br(d_inode(mod->h_root), mod->perm, mod->path); + if (unlikely(err)) + goto out; + + br = au_sbr(sb, bindex); + AuDebugOn(mod->h_root != au_br_dentry(br)); + if (br->br_perm == mod->perm) + return 0; /* success */ + + /* pre-allocate for non-fhsm --> fhsm */ + bf = NULL; + if (!au_br_fhsm(br->br_perm) && au_br_fhsm(mod->perm)) { + err = au_fhsm_br_alloc(br); + if (unlikely(err)) + goto out; + bf = br->br_fhsm; + br->br_fhsm = NULL; + } + + if (au_br_writable(br->br_perm)) { + /* remove whiteout base */ + err = au_br_init_wh(sb, br, mod->perm); + if (unlikely(err)) + goto out_bf; + + if (!au_br_writable(mod->perm)) { + /* rw --> ro, file might be mmapped */ + DiMustNoWaiters(root); + IiMustNoWaiters(d_inode(root)); + di_write_unlock(root); + err = au_br_mod_files_ro(sb, bindex); + /* aufs_write_lock() calls ..._child() */ + di_write_lock_child(root); + + if (unlikely(err)) { + rerr = -ENOMEM; + br->br_wbr = kzalloc(sizeof(*br->br_wbr), + GFP_NOFS); + if (br->br_wbr) + rerr = au_wbr_init(br, sb, br->br_perm); + if (unlikely(rerr)) { + AuIOErr("nested error %d (%d)\n", + rerr, err); + br->br_perm = mod->perm; + } + } + } + } else if (au_br_writable(mod->perm)) { + /* ro --> rw */ + err = -ENOMEM; + br->br_wbr = kzalloc(sizeof(*br->br_wbr), GFP_NOFS); + if (br->br_wbr) { + err = au_wbr_init(br, sb, mod->perm); + if (unlikely(err)) { + au_kfree_rcu(br->br_wbr); + br->br_wbr = NULL; + } + } + } + if (unlikely(err)) + goto out_bf; + + if (au_br_fhsm(br->br_perm)) { + if (!au_br_fhsm(mod->perm)) { + /* fhsm --> non-fhsm */ + au_br_fhsm_fin(br->br_fhsm); + au_kfree_rcu(br->br_fhsm); + br->br_fhsm = NULL; + } + } else if (au_br_fhsm(mod->perm)) + /* non-fhsm --> fhsm */ + br->br_fhsm = bf; + + *do_refresh |= need_sigen_inc(br->br_perm, mod->perm); + br->br_perm = mod->perm; + goto out; /* success */ + +out_bf: + au_kfree_try_rcu(bf); +out: + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +int au_br_stfs(struct au_branch *br, struct aufs_stfs *stfs) +{ + int err; + struct kstatfs kstfs; + + err = vfs_statfs(&br->br_path, &kstfs); + if (!err) { + stfs->f_blocks = kstfs.f_blocks; + stfs->f_bavail = kstfs.f_bavail; + stfs->f_files = kstfs.f_files; + stfs->f_ffree = kstfs.f_ffree; + } + + return err; +} diff --git a/fs/aufs/branch.h b/fs/aufs/branch.h new file mode 100644 index 0000000000000..fa48183e9a6b1 --- /dev/null +++ b/fs/aufs/branch.h @@ -0,0 +1,364 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * branch filesystems and xino for them + */ + +#ifndef __AUFS_BRANCH_H__ +#define __AUFS_BRANCH_H__ + +#ifdef __KERNEL__ + +#include +#include "dirren.h" +#include "dynop.h" +#include "lcnt.h" +#include "rwsem.h" +#include "super.h" + +/* ---------------------------------------------------------------------- */ + +/* a xino file */ +struct au_xino { + struct file **xi_file; + unsigned int xi_nfile; + + struct { + spinlock_t spin; + ino_t *array; + int total; + /* reserved for future use */ + /* unsigned long *bitmap; */ + wait_queue_head_t wqh; + } xi_nondir; + + struct mutex xi_mtx; /* protects xi_file array */ + struct hlist_bl_head xi_writing; + + atomic_t xi_truncating; + + struct kref xi_kref; +}; + +/* File-based Hierarchical Storage Management */ +struct au_br_fhsm { +#ifdef CONFIG_AUFS_FHSM + struct mutex bf_lock; + unsigned long bf_jiffy; + struct aufs_stfs bf_stfs; + int bf_readable; +#endif +}; + +/* members for writable branch only */ +enum {AuBrWh_BASE, AuBrWh_PLINK, AuBrWh_ORPH, AuBrWh_Last}; +struct au_wbr { + struct au_rwsem wbr_wh_rwsem; + struct dentry *wbr_wh[AuBrWh_Last]; + atomic_t wbr_wh_running; +#define wbr_whbase wbr_wh[AuBrWh_BASE] /* whiteout base */ +#define wbr_plink wbr_wh[AuBrWh_PLINK] /* pseudo-link dir */ +#define wbr_orph wbr_wh[AuBrWh_ORPH] /* dir for orphans */ + + /* mfs mode */ + unsigned long long wbr_bytes; +}; + +/* ext2 has 3 types of operations at least, ext3 has 4 */ +#define AuBrDynOp (AuDyLast * 4) + +#ifdef CONFIG_AUFS_HFSNOTIFY +/* support for asynchronous destruction */ +struct au_br_hfsnotify { + struct fsnotify_group *hfsn_group; +}; +#endif + +/* sysfs entries */ +struct au_brsysfs { + char name[16]; + struct attribute attr; +}; + +enum { + AuBrSysfs_BR, + AuBrSysfs_BRID, + AuBrSysfs_Last +}; + +/* protected by superblock rwsem */ +struct au_branch { + struct au_xino *br_xino; + + aufs_bindex_t br_id; + + int br_perm; + struct path br_path; + spinlock_t br_dykey_lock; + struct au_dykey *br_dykey[AuBrDynOp]; + au_lcnt_t br_nfiles; /* opened files */ + au_lcnt_t br_count; /* in-use for other */ + + struct au_wbr *br_wbr; + struct au_br_fhsm *br_fhsm; + +#ifdef CONFIG_AUFS_HFSNOTIFY + struct au_br_hfsnotify *br_hfsn; +#endif + +#ifdef CONFIG_SYSFS + /* entries under sysfs per mount-point */ + struct au_brsysfs br_sysfs[AuBrSysfs_Last]; +#endif + +#ifdef CONFIG_DEBUG_FS + struct dentry *br_dbgaufs; /* xino */ +#endif + + struct au_dr_br br_dirren; +}; + +/* ---------------------------------------------------------------------- */ + +static inline struct vfsmount *au_br_mnt(struct au_branch *br) +{ + return br->br_path.mnt; +} + +static inline struct dentry *au_br_dentry(struct au_branch *br) +{ + return br->br_path.dentry; +} + +static inline struct super_block *au_br_sb(struct au_branch *br) +{ + return au_br_mnt(br)->mnt_sb; +} + +static inline int au_br_rdonly(struct au_branch *br) +{ + return (sb_rdonly(au_br_sb(br)) + || !au_br_writable(br->br_perm)) + ? -EROFS : 0; +} + +static inline int au_br_hnotifyable(int brperm __maybe_unused) +{ +#ifdef CONFIG_AUFS_HNOTIFY + return !(brperm & AuBrPerm_RR); +#else + return 0; +#endif +} + +static inline int au_br_test_oflag(int oflag, struct au_branch *br) +{ + int err, exec_flag; + + err = 0; + exec_flag = oflag & __FMODE_EXEC; + if (unlikely(exec_flag && path_noexec(&br->br_path))) + err = -EACCES; + + return err; +} + +static inline void au_xino_get(struct au_branch *br) +{ + struct au_xino *xi; + + xi = br->br_xino; + if (xi) + kref_get(&xi->xi_kref); +} + +static inline int au_xino_count(struct au_branch *br) +{ + int v; + struct au_xino *xi; + + v = 0; + xi = br->br_xino; + if (xi) + v = kref_read(&xi->xi_kref); + + return v; +} + +/* ---------------------------------------------------------------------- */ + +/* branch.c */ +struct au_sbinfo; +void au_br_free(struct au_sbinfo *sinfo); +int au_br_index(struct super_block *sb, aufs_bindex_t br_id); +struct au_opt_add; +int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount); +struct au_opt_del; +int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount); +long au_ibusy_ioctl(struct file *file, unsigned long arg); +#ifdef CONFIG_COMPAT +long au_ibusy_compat_ioctl(struct file *file, unsigned long arg); +#endif +struct au_opt_mod; +int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount, + int *do_refresh); +struct aufs_stfs; +int au_br_stfs(struct au_branch *br, struct aufs_stfs *stfs); + +/* xino.c */ +static const loff_t au_loff_max = LLONG_MAX; + +aufs_bindex_t au_xi_root(struct super_block *sb, struct dentry *dentry); +struct file *au_xino_create(struct super_block *sb, char *fpath, int silent, + int wbrtop); +struct file *au_xino_create2(struct super_block *sb, struct path *base, + struct file *copy_src); +struct au_xi_new { + struct au_xino *xi; /* switch between xino and xigen */ + int idx; + struct path *base; + struct file *copy_src; +}; +struct file *au_xi_new(struct super_block *sb, struct au_xi_new *xinew); + +int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, + ino_t *ino); +int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, + ino_t ino); +ssize_t xino_fread(struct file *file, void *buf, size_t size, loff_t *pos); +ssize_t xino_fwrite(struct file *file, void *buf, size_t size, loff_t *pos); + +int au_xib_trunc(struct super_block *sb); +int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex, int idx_begin); + +struct au_xino *au_xino_alloc(unsigned int nfile); +int au_xino_put(struct au_branch *br); +struct file *au_xino_file1(struct au_xino *xi); + +struct au_opt_xino; +void au_xino_clr(struct super_block *sb); +int au_xino_set(struct super_block *sb, struct au_opt_xino *xiopt, int remount); +struct file *au_xino_def(struct super_block *sb); +int au_xino_init_br(struct super_block *sb, struct au_branch *br, ino_t hino, + struct path *base); + +ino_t au_xino_new_ino(struct super_block *sb); +void au_xino_delete_inode(struct inode *inode, const int unlinked); + +void au_xinondir_leave(struct super_block *sb, aufs_bindex_t bindex, + ino_t h_ino, int idx); +int au_xinondir_enter(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, + int *idx); + +int au_xino_path(struct seq_file *seq, struct file *file); + +/* ---------------------------------------------------------------------- */ + +/* @idx is signed to accept -1 meaning the first file */ +static inline struct file *au_xino_file(struct au_xino *xi, int idx) +{ + struct file *file; + + file = NULL; + if (!xi) + goto out; + + if (idx >= 0) { + if (idx < xi->xi_nfile) + file = xi->xi_file[idx]; + } else + file = au_xino_file1(xi); + +out: + return file; +} + +/* ---------------------------------------------------------------------- */ + +/* Superblock to branch */ +static inline +aufs_bindex_t au_sbr_id(struct super_block *sb, aufs_bindex_t bindex) +{ + return au_sbr(sb, bindex)->br_id; +} + +static inline +struct vfsmount *au_sbr_mnt(struct super_block *sb, aufs_bindex_t bindex) +{ + return au_br_mnt(au_sbr(sb, bindex)); +} + +static inline +struct super_block *au_sbr_sb(struct super_block *sb, aufs_bindex_t bindex) +{ + return au_br_sb(au_sbr(sb, bindex)); +} + +static inline int au_sbr_perm(struct super_block *sb, aufs_bindex_t bindex) +{ + return au_sbr(sb, bindex)->br_perm; +} + +static inline int au_sbr_whable(struct super_block *sb, aufs_bindex_t bindex) +{ + return au_br_whable(au_sbr_perm(sb, bindex)); +} + +/* ---------------------------------------------------------------------- */ + +#define wbr_wh_read_lock(wbr) au_rw_read_lock(&(wbr)->wbr_wh_rwsem) +#define wbr_wh_write_lock(wbr) au_rw_write_lock(&(wbr)->wbr_wh_rwsem) +#define wbr_wh_read_trylock(wbr) au_rw_read_trylock(&(wbr)->wbr_wh_rwsem) +#define wbr_wh_write_trylock(wbr) au_rw_write_trylock(&(wbr)->wbr_wh_rwsem) +/* +#define wbr_wh_read_trylock_nested(wbr) \ + au_rw_read_trylock_nested(&(wbr)->wbr_wh_rwsem) +#define wbr_wh_write_trylock_nested(wbr) \ + au_rw_write_trylock_nested(&(wbr)->wbr_wh_rwsem) +*/ + +#define wbr_wh_read_unlock(wbr) au_rw_read_unlock(&(wbr)->wbr_wh_rwsem) +#define wbr_wh_write_unlock(wbr) au_rw_write_unlock(&(wbr)->wbr_wh_rwsem) +#define wbr_wh_downgrade_lock(wbr) au_rw_dgrade_lock(&(wbr)->wbr_wh_rwsem) + +#define WbrWhMustNoWaiters(wbr) AuRwMustNoWaiters(&(wbr)->wbr_wh_rwsem) +#define WbrWhMustAnyLock(wbr) AuRwMustAnyLock(&(wbr)->wbr_wh_rwsem) +#define WbrWhMustWriteLock(wbr) AuRwMustWriteLock(&(wbr)->wbr_wh_rwsem) + +/* ---------------------------------------------------------------------- */ + +#ifdef CONFIG_AUFS_FHSM +static inline void au_br_fhsm_init(struct au_br_fhsm *brfhsm) +{ + mutex_init(&brfhsm->bf_lock); + brfhsm->bf_jiffy = 0; + brfhsm->bf_readable = 0; +} + +static inline void au_br_fhsm_fin(struct au_br_fhsm *brfhsm) +{ + mutex_destroy(&brfhsm->bf_lock); +} +#else +AuStubVoid(au_br_fhsm_init, struct au_br_fhsm *brfhsm) +AuStubVoid(au_br_fhsm_fin, struct au_br_fhsm *brfhsm) +#endif + +#endif /* __KERNEL__ */ +#endif /* __AUFS_BRANCH_H__ */ diff --git a/fs/aufs/conf.mk b/fs/aufs/conf.mk new file mode 100644 index 0000000000000..12782f8e0f381 --- /dev/null +++ b/fs/aufs/conf.mk @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: GPL-2.0 + +AuConfStr = CONFIG_AUFS_FS=${CONFIG_AUFS_FS} + +define AuConf +ifdef ${1} +AuConfStr += ${1}=${${1}} +endif +endef + +AuConfAll = BRANCH_MAX_127 BRANCH_MAX_511 BRANCH_MAX_1023 BRANCH_MAX_32767 \ + SBILIST \ + HNOTIFY HFSNOTIFY \ + EXPORT INO_T_64 \ + XATTR \ + FHSM \ + RDU \ + DIRREN \ + SHWH \ + BR_RAMFS \ + BR_FUSE POLL \ + BR_HFSPLUS \ + BDEV_LOOP \ + DEBUG MAGIC_SYSRQ +$(foreach i, ${AuConfAll}, \ + $(eval $(call AuConf,CONFIG_AUFS_${i}))) + +AuConfName = ${obj}/conf.str +${AuConfName}.tmp: FORCE + @echo ${AuConfStr} | tr ' ' '\n' | sed -e 's/^/"/' -e 's/$$/\\n"/' > $@ +${AuConfName}: ${AuConfName}.tmp + @diff -q $< $@ > /dev/null 2>&1 || { \ + echo ' GEN ' $@; \ + cp -p $< $@; \ + } +FORCE: +clean-files += ${AuConfName} ${AuConfName}.tmp +${obj}/sysfs.o: ${AuConfName} + +-include ${srctree}/${src}/conf_priv.mk diff --git a/fs/aufs/cpup.c b/fs/aufs/cpup.c new file mode 100644 index 0000000000000..1ba7e8a66db76 --- /dev/null +++ b/fs/aufs/cpup.c @@ -0,0 +1,1447 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * copy-up functions, see wbr_policy.c for copy-down + */ + +#include +#include +#include +#include "aufs.h" + +void au_cpup_attr_flags(struct inode *dst, unsigned int iflags) +{ + const unsigned int mask = S_DEAD | S_SWAPFILE | S_PRIVATE + | S_NOATIME | S_NOCMTIME | S_AUTOMOUNT; + + BUILD_BUG_ON(sizeof(iflags) != sizeof(dst->i_flags)); + + dst->i_flags |= iflags & ~mask; + if (au_test_fs_notime(dst->i_sb)) + dst->i_flags |= S_NOATIME | S_NOCMTIME; +} + +void au_cpup_attr_timesizes(struct inode *inode) +{ + struct inode *h_inode; + + h_inode = au_h_iptr(inode, au_ibtop(inode)); + fsstack_copy_attr_times(inode, h_inode); + fsstack_copy_inode_size(inode, h_inode); +} + +void au_cpup_attr_nlink(struct inode *inode, int force) +{ + struct inode *h_inode; + struct super_block *sb; + aufs_bindex_t bindex, bbot; + + sb = inode->i_sb; + bindex = au_ibtop(inode); + h_inode = au_h_iptr(inode, bindex); + if (!force + && !S_ISDIR(h_inode->i_mode) + && au_opt_test(au_mntflags(sb), PLINK) + && au_plink_test(inode)) + return; + + /* + * 0 can happen in revalidating. + * h_inode->i_mutex may not be held here, but it is harmless since once + * i_nlink reaches 0, it will never become positive except O_TMPFILE + * case. + * todo: O_TMPFILE+linkat(AT_SYMLINK_FOLLOW) bypassing aufs may cause + * the incorrect link count. + */ + set_nlink(inode, h_inode->i_nlink); + + /* + * fewer nlink makes find(1) noisy, but larger nlink doesn't. + * it may includes whplink directory. + */ + if (S_ISDIR(h_inode->i_mode)) { + bbot = au_ibbot(inode); + for (bindex++; bindex <= bbot; bindex++) { + h_inode = au_h_iptr(inode, bindex); + if (h_inode) + au_add_nlink(inode, h_inode); + } + } +} + +void au_cpup_attr_changeable(struct inode *inode) +{ + struct inode *h_inode; + + h_inode = au_h_iptr(inode, au_ibtop(inode)); + inode->i_mode = h_inode->i_mode; + inode->i_uid = h_inode->i_uid; + inode->i_gid = h_inode->i_gid; + au_cpup_attr_timesizes(inode); + au_cpup_attr_flags(inode, h_inode->i_flags); +} + +void au_cpup_igen(struct inode *inode, struct inode *h_inode) +{ + struct au_iinfo *iinfo = au_ii(inode); + + IiMustWriteLock(inode); + + iinfo->ii_higen = h_inode->i_generation; + iinfo->ii_hsb1 = h_inode->i_sb; +} + +void au_cpup_attr_all(struct inode *inode, int force) +{ + struct inode *h_inode; + + h_inode = au_h_iptr(inode, au_ibtop(inode)); + au_cpup_attr_changeable(inode); + if (inode->i_nlink > 0) + au_cpup_attr_nlink(inode, force); + inode->i_rdev = h_inode->i_rdev; + inode->i_blkbits = h_inode->i_blkbits; + au_cpup_igen(inode, h_inode); +} + +/* ---------------------------------------------------------------------- */ + +/* Note: dt_dentry and dt_h_dentry are not dget/dput-ed */ + +/* keep the timestamps of the parent dir when cpup */ +void au_dtime_store(struct au_dtime *dt, struct dentry *dentry, + struct path *h_path) +{ + struct inode *h_inode; + + dt->dt_dentry = dentry; + dt->dt_h_path = *h_path; + h_inode = d_inode(h_path->dentry); + dt->dt_atime = h_inode->i_atime; + dt->dt_mtime = h_inode->i_mtime; + /* smp_mb(); */ +} + +void au_dtime_revert(struct au_dtime *dt) +{ + struct iattr attr; + int err; + + attr.ia_atime = dt->dt_atime; + attr.ia_mtime = dt->dt_mtime; + attr.ia_valid = ATTR_FORCE | ATTR_MTIME | ATTR_MTIME_SET + | ATTR_ATIME | ATTR_ATIME_SET; + + /* no delegation since this is a directory */ + err = vfsub_notify_change(&dt->dt_h_path, &attr, /*delegated*/NULL); + if (unlikely(err)) + pr_warn("restoring timestamps failed(%d). ignored\n", err); +} + +/* ---------------------------------------------------------------------- */ + +/* internal use only */ +struct au_cpup_reg_attr { + int valid; + struct kstat st; + unsigned int iflags; /* inode->i_flags */ +}; + +static noinline_for_stack +int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src, + struct au_cpup_reg_attr *h_src_attr) +{ + int err, sbits, icex; + unsigned int mnt_flags; + unsigned char verbose; + struct iattr ia; + struct path h_path; + struct inode *h_isrc, *h_idst; + struct kstat *h_st; + struct au_branch *br; + + h_path.dentry = au_h_dptr(dst, bindex); + h_idst = d_inode(h_path.dentry); + br = au_sbr(dst->d_sb, bindex); + h_path.mnt = au_br_mnt(br); + h_isrc = d_inode(h_src); + ia.ia_valid = ATTR_FORCE | ATTR_UID | ATTR_GID + | ATTR_ATIME | ATTR_MTIME + | ATTR_ATIME_SET | ATTR_MTIME_SET; + if (h_src_attr && h_src_attr->valid) { + h_st = &h_src_attr->st; + ia.ia_uid = h_st->uid; + ia.ia_gid = h_st->gid; + ia.ia_atime = h_st->atime; + ia.ia_mtime = h_st->mtime; + if (h_idst->i_mode != h_st->mode + && !S_ISLNK(h_idst->i_mode)) { + ia.ia_valid |= ATTR_MODE; + ia.ia_mode = h_st->mode; + } + sbits = !!(h_st->mode & (S_ISUID | S_ISGID)); + au_cpup_attr_flags(h_idst, h_src_attr->iflags); + } else { + ia.ia_uid = h_isrc->i_uid; + ia.ia_gid = h_isrc->i_gid; + ia.ia_atime = h_isrc->i_atime; + ia.ia_mtime = h_isrc->i_mtime; + if (h_idst->i_mode != h_isrc->i_mode + && !S_ISLNK(h_idst->i_mode)) { + ia.ia_valid |= ATTR_MODE; + ia.ia_mode = h_isrc->i_mode; + } + sbits = !!(h_isrc->i_mode & (S_ISUID | S_ISGID)); + au_cpup_attr_flags(h_idst, h_isrc->i_flags); + } + /* no delegation since it is just created */ + err = vfsub_notify_change(&h_path, &ia, /*delegated*/NULL); + + /* is this nfs only? */ + if (!err && sbits && au_test_nfs(h_path.dentry->d_sb)) { + ia.ia_valid = ATTR_FORCE | ATTR_MODE; + ia.ia_mode = h_isrc->i_mode; + err = vfsub_notify_change(&h_path, &ia, /*delegated*/NULL); + } + + icex = br->br_perm & AuBrAttr_ICEX; + if (!err) { + mnt_flags = au_mntflags(dst->d_sb); + verbose = !!au_opt_test(mnt_flags, VERBOSE); + err = au_cpup_xattr(h_path.dentry, h_src, icex, verbose); + } + + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int au_do_copy_file(struct file *dst, struct file *src, loff_t len, + char *buf, unsigned long blksize) +{ + int err; + size_t sz, rbytes, wbytes; + unsigned char all_zero; + char *p, *zp; + struct inode *h_inode; + /* reduce stack usage */ + struct iattr *ia; + + zp = page_address(ZERO_PAGE(0)); + if (unlikely(!zp)) + return -ENOMEM; /* possible? */ + + err = 0; + all_zero = 0; + while (len) { + AuDbg("len %lld\n", len); + sz = blksize; + if (len < blksize) + sz = len; + + rbytes = 0; + /* todo: signal_pending? */ + while (!rbytes || err == -EAGAIN || err == -EINTR) { + rbytes = vfsub_read_k(src, buf, sz, &src->f_pos); + err = rbytes; + } + if (unlikely(err < 0)) + break; + + all_zero = 0; + if (len >= rbytes && rbytes == blksize) + all_zero = !memcmp(buf, zp, rbytes); + if (!all_zero) { + wbytes = rbytes; + p = buf; + while (wbytes) { + size_t b; + + b = vfsub_write_k(dst, p, wbytes, &dst->f_pos); + err = b; + /* todo: signal_pending? */ + if (unlikely(err == -EAGAIN || err == -EINTR)) + continue; + if (unlikely(err < 0)) + break; + wbytes -= b; + p += b; + } + if (unlikely(err < 0)) + break; + } else { + loff_t res; + + AuLabel(hole); + res = vfsub_llseek(dst, rbytes, SEEK_CUR); + err = res; + if (unlikely(res < 0)) + break; + } + len -= rbytes; + err = 0; + } + + /* the last block may be a hole */ + if (!err && all_zero) { + AuLabel(last hole); + + err = 1; + if (au_test_nfs(dst->f_path.dentry->d_sb)) { + /* nfs requires this step to make last hole */ + /* is this only nfs? */ + do { + /* todo: signal_pending? */ + err = vfsub_write_k(dst, "\0", 1, &dst->f_pos); + } while (err == -EAGAIN || err == -EINTR); + if (err == 1) + dst->f_pos--; + } + + if (err == 1) { + ia = (void *)buf; + ia->ia_size = dst->f_pos; + ia->ia_valid = ATTR_SIZE | ATTR_FILE; + ia->ia_file = dst; + h_inode = file_inode(dst); + inode_lock_nested(h_inode, AuLsc_I_CHILD2); + /* no delegation since it is just created */ + err = vfsub_notify_change(&dst->f_path, ia, + /*delegated*/NULL); + inode_unlock(h_inode); + } + } + + return err; +} + +int au_copy_file(struct file *dst, struct file *src, loff_t len) +{ + int err; + unsigned long blksize; + unsigned char do_kfree; + char *buf; + struct super_block *h_sb; + + err = -ENOMEM; + h_sb = file_inode(dst)->i_sb; + blksize = h_sb->s_blocksize; + if (!blksize || PAGE_SIZE < blksize) + blksize = PAGE_SIZE; + AuDbg("blksize %lu\n", blksize); + do_kfree = (blksize != PAGE_SIZE && blksize >= sizeof(struct iattr *)); + if (do_kfree) + buf = kmalloc(blksize, GFP_NOFS); + else + buf = (void *)__get_free_page(GFP_NOFS); + if (unlikely(!buf)) + goto out; + + if (len > (1 << 22)) + AuDbg("copying a large file %lld\n", (long long)len); + + src->f_pos = 0; + dst->f_pos = 0; + err = au_do_copy_file(dst, src, len, buf, blksize); + if (do_kfree) { + AuDebugOn(!au_kfree_do_sz_test(blksize)); + au_kfree_do_rcu(buf); + } else + free_page((unsigned long)buf); + +out: + return err; +} + +static int au_do_copy(struct file *dst, struct file *src, loff_t len) +{ + int err; + struct super_block *h_src_sb; + struct inode *h_src_inode; + + h_src_inode = file_inode(src); + h_src_sb = h_src_inode->i_sb; + + /* XFS acquires inode_lock */ + if (!au_test_xfs(h_src_sb)) + err = au_copy_file(dst, src, len); + else { + inode_unlock_shared(h_src_inode); + err = au_copy_file(dst, src, len); + inode_lock_shared_nested(h_src_inode, AuLsc_I_CHILD); + } + + return err; +} + +static int au_clone_or_copy(struct file *dst, struct file *src, loff_t len) +{ + int err; + loff_t lo; + struct super_block *h_src_sb; + struct inode *h_src_inode; + + h_src_inode = file_inode(src); + h_src_sb = h_src_inode->i_sb; + if (h_src_sb != file_inode(dst)->i_sb + || !dst->f_op->remap_file_range) { + err = au_do_copy(dst, src, len); + goto out; + } + + if (!au_test_nfs(h_src_sb)) { + inode_unlock_shared(h_src_inode); + lo = vfsub_clone_file_range(src, dst, len); + inode_lock_shared_nested(h_src_inode, AuLsc_I_CHILD); + } else + lo = vfsub_clone_file_range(src, dst, len); + if (lo == len) { + err = 0; + goto out; /* success */ + } else if (lo >= 0) + /* todo: possible? */ + /* paritially succeeded */ + AuDbg("lo %lld, len %lld. Retrying.\n", lo, len); + else if (lo != -EOPNOTSUPP) { + /* older XFS has a condition in cloning */ + err = lo; + goto out; + } + + /* the backend fs on NFS may not support cloning */ + err = au_do_copy(dst, src, len); + +out: + AuTraceErr(err); + return err; +} + +/* + * to support a sparse file which is opened with O_APPEND, + * we need to close the file. + */ +static int au_cp_regular(struct au_cp_generic *cpg) +{ + int err, i; + enum { SRC, DST }; + struct { + aufs_bindex_t bindex; + unsigned int flags; + struct dentry *dentry; + int force_wr; + struct file *file; + } *f, file[] = { + { + .bindex = cpg->bsrc, + .flags = O_RDONLY | O_NOATIME | O_LARGEFILE, + }, + { + .bindex = cpg->bdst, + .flags = O_WRONLY | O_NOATIME | O_LARGEFILE, + .force_wr = !!au_ftest_cpup(cpg->flags, RWDST), + } + }; + struct au_branch *br; + struct super_block *sb, *h_src_sb; + struct inode *h_src_inode; + struct task_struct *tsk = current; + + /* bsrc branch can be ro/rw. */ + sb = cpg->dentry->d_sb; + f = file; + for (i = 0; i < 2; i++, f++) { + f->dentry = au_h_dptr(cpg->dentry, f->bindex); + f->file = au_h_open(cpg->dentry, f->bindex, f->flags, + /*file*/NULL, f->force_wr); + if (IS_ERR(f->file)) { + err = PTR_ERR(f->file); + if (i == SRC) + goto out; + else + goto out_src; + } + } + + /* try stopping to update while we copyup */ + h_src_inode = d_inode(file[SRC].dentry); + h_src_sb = h_src_inode->i_sb; + if (!au_test_nfs(h_src_sb)) + IMustLock(h_src_inode); + err = au_clone_or_copy(file[DST].file, file[SRC].file, cpg->len); + + /* i wonder if we had O_NO_DELAY_FPUT flag */ + if (tsk->flags & PF_KTHREAD) + __fput_sync(file[DST].file); + else { + /* it happened actually */ + fput(file[DST].file); + /* + * too bad. + * we have to call both since we don't know which place the file + * was added to. + */ + task_work_run(); + flush_delayed_fput(); + } + br = au_sbr(sb, file[DST].bindex); + au_lcnt_dec(&br->br_nfiles); + +out_src: + fput(file[SRC].file); + br = au_sbr(sb, file[SRC].bindex); + au_lcnt_dec(&br->br_nfiles); +out: + return err; +} + +static int au_do_cpup_regular(struct au_cp_generic *cpg, + struct au_cpup_reg_attr *h_src_attr) +{ + int err, rerr; + loff_t l; + struct path h_path; + struct inode *h_src_inode, *h_dst_inode; + + err = 0; + h_src_inode = au_h_iptr(d_inode(cpg->dentry), cpg->bsrc); + l = i_size_read(h_src_inode); + if (cpg->len == -1 || l < cpg->len) + cpg->len = l; + if (cpg->len) { + /* try stopping to update while we are referencing */ + inode_lock_shared_nested(h_src_inode, AuLsc_I_CHILD); + au_pin_hdir_unlock(cpg->pin); + + h_path.dentry = au_h_dptr(cpg->dentry, cpg->bsrc); + h_path.mnt = au_sbr_mnt(cpg->dentry->d_sb, cpg->bsrc); + h_src_attr->iflags = h_src_inode->i_flags; + if (!au_test_nfs(h_src_inode->i_sb)) + err = vfsub_getattr(&h_path, &h_src_attr->st); + else { + inode_unlock_shared(h_src_inode); + err = vfsub_getattr(&h_path, &h_src_attr->st); + inode_lock_shared_nested(h_src_inode, AuLsc_I_CHILD); + } + if (unlikely(err)) { + inode_unlock_shared(h_src_inode); + goto out; + } + h_src_attr->valid = 1; + if (!au_test_nfs(h_src_inode->i_sb)) { + err = au_cp_regular(cpg); + inode_unlock_shared(h_src_inode); + } else { + inode_unlock_shared(h_src_inode); + err = au_cp_regular(cpg); + } + rerr = au_pin_hdir_relock(cpg->pin); + if (!err && rerr) + err = rerr; + } + if (!err && (h_src_inode->i_state & I_LINKABLE)) { + h_path.dentry = au_h_dptr(cpg->dentry, cpg->bdst); + h_dst_inode = d_inode(h_path.dentry); + spin_lock(&h_dst_inode->i_lock); + h_dst_inode->i_state |= I_LINKABLE; + spin_unlock(&h_dst_inode->i_lock); + } + +out: + return err; +} + +static int au_do_cpup_symlink(struct path *h_path, struct dentry *h_src, + struct inode *h_dir) +{ + int err; + DEFINE_DELAYED_CALL(done); + const char *sym; + + sym = vfs_get_link(h_src, &done); + err = PTR_ERR(sym); + if (IS_ERR(sym)) + goto out; + + err = vfsub_symlink(h_dir, h_path, sym); + +out: + do_delayed_call(&done); + return err; +} + +/* + * regardless 'acl' option, reset all ACL. + * All ACL will be copied up later from the original entry on the lower branch. + */ +static int au_reset_acl(struct path *h_path, umode_t mode) +{ + int err; + struct dentry *h_dentry; + struct inode *h_inode; + + h_dentry = h_path->dentry; + h_inode = d_inode(h_dentry); + /* forget_all_cached_acls(h_inode)); */ + err = vfsub_removexattr(h_dentry, XATTR_NAME_POSIX_ACL_ACCESS); + AuTraceErr(err); + if (err == -EOPNOTSUPP) + err = 0; + if (!err) + err = vfsub_acl_chmod(h_inode, mode); + + AuTraceErr(err); + return err; +} + +static int au_do_cpup_dir(struct au_cp_generic *cpg, struct dentry *dst_parent, + struct inode *h_dir, struct path *h_path) +{ + int err; + struct inode *dir, *inode; + + err = vfsub_removexattr(h_path->dentry, XATTR_NAME_POSIX_ACL_DEFAULT); + AuTraceErr(err); + if (err == -EOPNOTSUPP) + err = 0; + if (unlikely(err)) + goto out; + + /* + * strange behaviour from the users view, + * particularly setattr case + */ + dir = d_inode(dst_parent); + if (au_ibtop(dir) == cpg->bdst) + au_cpup_attr_nlink(dir, /*force*/1); + inode = d_inode(cpg->dentry); + au_cpup_attr_nlink(inode, /*force*/1); + +out: + return err; +} + +static noinline_for_stack +int cpup_entry(struct au_cp_generic *cpg, struct dentry *dst_parent, + struct au_cpup_reg_attr *h_src_attr) +{ + int err; + umode_t mode; + unsigned int mnt_flags; + unsigned char isdir, isreg, force; + const unsigned char do_dt = !!au_ftest_cpup(cpg->flags, DTIME); + struct au_dtime dt; + struct path h_path; + struct dentry *h_src, *h_dst, *h_parent; + struct inode *h_inode, *h_dir; + struct super_block *sb; + + /* bsrc branch can be ro/rw. */ + h_src = au_h_dptr(cpg->dentry, cpg->bsrc); + h_inode = d_inode(h_src); + AuDebugOn(h_inode != au_h_iptr(d_inode(cpg->dentry), cpg->bsrc)); + + /* try stopping to be referenced while we are creating */ + h_dst = au_h_dptr(cpg->dentry, cpg->bdst); + if (au_ftest_cpup(cpg->flags, RENAME)) + AuDebugOn(strncmp(h_dst->d_name.name, AUFS_WH_PFX, + AUFS_WH_PFX_LEN)); + h_parent = h_dst->d_parent; /* dir inode is locked */ + h_dir = d_inode(h_parent); + IMustLock(h_dir); + AuDebugOn(h_parent != h_dst->d_parent); + + sb = cpg->dentry->d_sb; + h_path.mnt = au_sbr_mnt(sb, cpg->bdst); + if (do_dt) { + h_path.dentry = h_parent; + au_dtime_store(&dt, dst_parent, &h_path); + } + h_path.dentry = h_dst; + + isreg = 0; + isdir = 0; + mode = h_inode->i_mode; + switch (mode & S_IFMT) { + case S_IFREG: + isreg = 1; + err = vfsub_create(h_dir, &h_path, 0600, /*want_excl*/true); + if (!err) + err = au_do_cpup_regular(cpg, h_src_attr); + break; + case S_IFDIR: + isdir = 1; + err = vfsub_mkdir(h_dir, &h_path, mode); + if (!err) + err = au_do_cpup_dir(cpg, dst_parent, h_dir, &h_path); + break; + case S_IFLNK: + err = au_do_cpup_symlink(&h_path, h_src, h_dir); + break; + case S_IFCHR: + case S_IFBLK: + AuDebugOn(!capable(CAP_MKNOD)); + fallthrough; + case S_IFIFO: + case S_IFSOCK: + err = vfsub_mknod(h_dir, &h_path, mode, h_inode->i_rdev); + break; + default: + AuIOErr("Unknown inode type 0%o\n", mode); + err = -EIO; + } + if (!err) + err = au_reset_acl(&h_path, mode); + + mnt_flags = au_mntflags(sb); + if (!au_opt_test(mnt_flags, UDBA_NONE) + && !isdir + && au_opt_test(mnt_flags, XINO) + && (h_inode->i_nlink == 1 + || (h_inode->i_state & I_LINKABLE)) + /* todo: unnecessary? */ + /* && d_inode(cpg->dentry)->i_nlink == 1 */ + && cpg->bdst < cpg->bsrc + && !au_ftest_cpup(cpg->flags, KEEPLINO)) + au_xino_write(sb, cpg->bsrc, h_inode->i_ino, /*ino*/0); + /* ignore this error */ + + if (!err) { + force = 0; + if (isreg) { + force = !!cpg->len; + if (cpg->len == -1) + force = !!i_size_read(h_inode); + } + au_fhsm_wrote(sb, cpg->bdst, force); + } + + if (do_dt) + au_dtime_revert(&dt); + return err; +} + +static int au_do_ren_after_cpup(struct au_cp_generic *cpg, struct path *h_path) +{ + int err; + struct dentry *dentry, *h_dentry, *h_parent, *parent; + struct path h_ppath; + struct inode *h_dir; + aufs_bindex_t bdst; + + dentry = cpg->dentry; + bdst = cpg->bdst; + h_ppath.mnt = au_sbr_mnt(dentry->d_sb, bdst); + h_dentry = au_h_dptr(dentry, bdst); + if (!au_ftest_cpup(cpg->flags, OVERWRITE)) { + dget(h_dentry); + au_set_h_dptr(dentry, bdst, NULL); + err = au_lkup_neg(dentry, bdst, /*wh*/0); + if (!err) + h_path->dentry = dget(au_h_dptr(dentry, bdst)); + au_set_h_dptr(dentry, bdst, h_dentry); + } else { + err = 0; + parent = dget_parent(dentry); + h_ppath.dentry = au_h_dptr(parent, bdst); + dput(parent); + h_path->dentry = vfsub_lkup_one(&dentry->d_name, &h_ppath); + if (IS_ERR(h_path->dentry)) + err = PTR_ERR(h_path->dentry); + } + if (unlikely(err)) + goto out; + + h_parent = h_dentry->d_parent; /* dir inode is locked */ + h_dir = d_inode(h_parent); + IMustLock(h_dir); + AuDbg("%pd %pd\n", h_dentry, h_path->dentry); + /* no delegation since it is just created */ + err = vfsub_rename(h_dir, h_dentry, h_dir, h_path, /*delegated*/NULL, + /*flags*/0); + dput(h_path->dentry); + +out: + return err; +} + +/* + * copyup the @dentry from @bsrc to @bdst. + * the caller must set the both of lower dentries. + * @len is for truncating when it is -1 copyup the entire file. + * in link/rename cases, @dst_parent may be different from the real one. + * basic->bsrc can be larger than basic->bdst. + * aufs doesn't touch the credential so + * security_inode_copy_up{,_xattr}() are unnecessary. + */ +static int au_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent) +{ + int err, rerr; + aufs_bindex_t old_ibtop; + unsigned char isdir, plink; + struct dentry *h_src, *h_dst, *h_parent; + struct inode *dst_inode, *h_dir, *inode, *delegated, *src_inode; + struct super_block *sb; + struct au_branch *br; + /* to reduce stack size */ + struct { + struct au_dtime dt; + struct path h_path; + struct au_cpup_reg_attr h_src_attr; + } *a; + + err = -ENOMEM; + a = kmalloc(sizeof(*a), GFP_NOFS); + if (unlikely(!a)) + goto out; + a->h_src_attr.valid = 0; + + sb = cpg->dentry->d_sb; + br = au_sbr(sb, cpg->bdst); + a->h_path.mnt = au_br_mnt(br); + h_dst = au_h_dptr(cpg->dentry, cpg->bdst); + h_parent = h_dst->d_parent; /* dir inode is locked */ + h_dir = d_inode(h_parent); + IMustLock(h_dir); + + h_src = au_h_dptr(cpg->dentry, cpg->bsrc); + inode = d_inode(cpg->dentry); + + if (!dst_parent) + dst_parent = dget_parent(cpg->dentry); + else + dget(dst_parent); + + plink = !!au_opt_test(au_mntflags(sb), PLINK); + dst_inode = au_h_iptr(inode, cpg->bdst); + if (dst_inode) { + if (unlikely(!plink)) { + err = -EIO; + AuIOErr("hi%lu(i%lu) exists on b%d " + "but plink is disabled\n", + dst_inode->i_ino, inode->i_ino, cpg->bdst); + goto out_parent; + } + + if (dst_inode->i_nlink) { + const int do_dt = au_ftest_cpup(cpg->flags, DTIME); + + h_src = au_plink_lkup(inode, cpg->bdst); + err = PTR_ERR(h_src); + if (IS_ERR(h_src)) + goto out_parent; + if (unlikely(d_is_negative(h_src))) { + err = -EIO; + AuIOErr("i%lu exists on b%d " + "but not pseudo-linked\n", + inode->i_ino, cpg->bdst); + dput(h_src); + goto out_parent; + } + + if (do_dt) { + a->h_path.dentry = h_parent; + au_dtime_store(&a->dt, dst_parent, &a->h_path); + } + + a->h_path.dentry = h_dst; + delegated = NULL; + err = vfsub_link(h_src, h_dir, &a->h_path, &delegated); + if (!err && au_ftest_cpup(cpg->flags, RENAME)) + err = au_do_ren_after_cpup(cpg, &a->h_path); + if (do_dt) + au_dtime_revert(&a->dt); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal link\n"); + iput(delegated); + } + dput(h_src); + goto out_parent; + } else + /* todo: cpup_wh_file? */ + /* udba work */ + au_update_ibrange(inode, /*do_put_zero*/1); + } + + isdir = S_ISDIR(inode->i_mode); + old_ibtop = au_ibtop(inode); + err = cpup_entry(cpg, dst_parent, &a->h_src_attr); + if (unlikely(err)) + goto out_rev; + dst_inode = d_inode(h_dst); + inode_lock_nested(dst_inode, AuLsc_I_CHILD2); + /* todo: necessary? */ + /* au_pin_hdir_unlock(cpg->pin); */ + + err = cpup_iattr(cpg->dentry, cpg->bdst, h_src, &a->h_src_attr); + if (unlikely(err)) { + /* todo: necessary? */ + /* au_pin_hdir_relock(cpg->pin); */ /* ignore an error */ + inode_unlock(dst_inode); + goto out_rev; + } + + if (cpg->bdst < old_ibtop) { + if (S_ISREG(inode->i_mode)) { + err = au_dy_iaop(inode, cpg->bdst, dst_inode); + if (unlikely(err)) { + /* ignore an error */ + /* au_pin_hdir_relock(cpg->pin); */ + inode_unlock(dst_inode); + goto out_rev; + } + } + au_set_ibtop(inode, cpg->bdst); + } else + au_set_ibbot(inode, cpg->bdst); + au_set_h_iptr(inode, cpg->bdst, au_igrab(dst_inode), + au_hi_flags(inode, isdir)); + + /* todo: necessary? */ + /* err = au_pin_hdir_relock(cpg->pin); */ + inode_unlock(dst_inode); + if (unlikely(err)) + goto out_rev; + + src_inode = d_inode(h_src); + if (!isdir + && (src_inode->i_nlink > 1 + || src_inode->i_state & I_LINKABLE) + && plink) + au_plink_append(inode, cpg->bdst, h_dst); + + if (au_ftest_cpup(cpg->flags, RENAME)) { + a->h_path.dentry = h_dst; + err = au_do_ren_after_cpup(cpg, &a->h_path); + } + if (!err) + goto out_parent; /* success */ + + /* revert */ +out_rev: + a->h_path.dentry = h_parent; + au_dtime_store(&a->dt, dst_parent, &a->h_path); + a->h_path.dentry = h_dst; + rerr = 0; + if (d_is_positive(h_dst)) { + if (!isdir) { + /* no delegation since it is just created */ + rerr = vfsub_unlink(h_dir, &a->h_path, + /*delegated*/NULL, /*force*/0); + } else + rerr = vfsub_rmdir(h_dir, &a->h_path); + } + au_dtime_revert(&a->dt); + if (rerr) { + AuIOErr("failed removing broken entry(%d, %d)\n", err, rerr); + err = -EIO; + } +out_parent: + dput(dst_parent); + au_kfree_rcu(a); +out: + return err; +} + +#if 0 /* reserved */ +struct au_cpup_single_args { + int *errp; + struct au_cp_generic *cpg; + struct dentry *dst_parent; +}; + +static void au_call_cpup_single(void *args) +{ + struct au_cpup_single_args *a = args; + + au_pin_hdir_acquire_nest(a->cpg->pin); + *a->errp = au_cpup_single(a->cpg, a->dst_parent); + au_pin_hdir_release(a->cpg->pin); +} +#endif + +/* + * prevent SIGXFSZ in copy-up. + * testing CAP_MKNOD is for generic fs, + * but CAP_FSETID is for xfs only, currently. + */ +static int au_cpup_sio_test(struct au_pin *pin, umode_t mode) +{ + int do_sio; + struct super_block *sb; + struct inode *h_dir; + + do_sio = 0; + sb = au_pinned_parent(pin)->d_sb; + if (!au_wkq_test() + && (!au_sbi(sb)->si_plink_maint_pid + || au_plink_maint(sb, AuLock_NOPLM))) { + switch (mode & S_IFMT) { + case S_IFREG: + /* no condition about RLIMIT_FSIZE and the file size */ + do_sio = 1; + break; + case S_IFCHR: + case S_IFBLK: + do_sio = !capable(CAP_MKNOD); + break; + } + if (!do_sio) + do_sio = ((mode & (S_ISUID | S_ISGID)) + && !capable(CAP_FSETID)); + /* this workaround may be removed in the future */ + if (!do_sio) { + h_dir = au_pinned_h_dir(pin); + do_sio = h_dir->i_mode & S_ISVTX; + } + } + + return do_sio; +} + +#if 0 /* reserved */ +int au_sio_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent) +{ + int err, wkq_err; + struct dentry *h_dentry; + + h_dentry = au_h_dptr(cpg->dentry, cpg->bsrc); + if (!au_cpup_sio_test(pin, d_inode(h_dentry)->i_mode)) + err = au_cpup_single(cpg, dst_parent); + else { + struct au_cpup_single_args args = { + .errp = &err, + .cpg = cpg, + .dst_parent = dst_parent + }; + wkq_err = au_wkq_wait(au_call_cpup_single, &args); + if (unlikely(wkq_err)) + err = wkq_err; + } + + return err; +} +#endif + +/* + * copyup the @dentry from the first active lower branch to @bdst, + * using au_cpup_single(). + */ +static int au_cpup_simple(struct au_cp_generic *cpg) +{ + int err; + unsigned int flags_orig; + struct dentry *dentry; + + AuDebugOn(cpg->bsrc < 0); + + dentry = cpg->dentry; + DiMustWriteLock(dentry); + + err = au_lkup_neg(dentry, cpg->bdst, /*wh*/1); + if (!err) { + flags_orig = cpg->flags; + au_fset_cpup(cpg->flags, RENAME); + err = au_cpup_single(cpg, NULL); + cpg->flags = flags_orig; + if (!err) + return 0; /* success */ + + /* revert */ + au_set_h_dptr(dentry, cpg->bdst, NULL); + au_set_dbtop(dentry, cpg->bsrc); + } + + return err; +} + +struct au_cpup_simple_args { + int *errp; + struct au_cp_generic *cpg; +}; + +static void au_call_cpup_simple(void *args) +{ + struct au_cpup_simple_args *a = args; + + au_pin_hdir_acquire_nest(a->cpg->pin); + *a->errp = au_cpup_simple(a->cpg); + au_pin_hdir_release(a->cpg->pin); +} + +static int au_do_sio_cpup_simple(struct au_cp_generic *cpg) +{ + int err, wkq_err; + struct dentry *dentry, *parent; + struct file *h_file; + struct inode *h_dir; + + dentry = cpg->dentry; + h_file = NULL; + if (au_ftest_cpup(cpg->flags, HOPEN)) { + AuDebugOn(cpg->bsrc < 0); + h_file = au_h_open_pre(dentry, cpg->bsrc, /*force_wr*/0); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; + } + + parent = dget_parent(dentry); + h_dir = au_h_iptr(d_inode(parent), cpg->bdst); + if (!au_test_h_perm_sio(h_dir, MAY_EXEC | MAY_WRITE) + && !au_cpup_sio_test(cpg->pin, d_inode(dentry)->i_mode)) + err = au_cpup_simple(cpg); + else { + struct au_cpup_simple_args args = { + .errp = &err, + .cpg = cpg + }; + wkq_err = au_wkq_wait(au_call_cpup_simple, &args); + if (unlikely(wkq_err)) + err = wkq_err; + } + + dput(parent); + if (h_file) + au_h_open_post(dentry, cpg->bsrc, h_file); + +out: + return err; +} + +int au_sio_cpup_simple(struct au_cp_generic *cpg) +{ + aufs_bindex_t bsrc, bbot; + struct dentry *dentry, *h_dentry; + + if (cpg->bsrc < 0) { + dentry = cpg->dentry; + bbot = au_dbbot(dentry); + for (bsrc = cpg->bdst + 1; bsrc <= bbot; bsrc++) { + h_dentry = au_h_dptr(dentry, bsrc); + if (h_dentry) { + AuDebugOn(d_is_negative(h_dentry)); + break; + } + } + AuDebugOn(bsrc > bbot); + cpg->bsrc = bsrc; + } + AuDebugOn(cpg->bsrc <= cpg->bdst); + return au_do_sio_cpup_simple(cpg); +} + +int au_sio_cpdown_simple(struct au_cp_generic *cpg) +{ + AuDebugOn(cpg->bdst <= cpg->bsrc); + return au_do_sio_cpup_simple(cpg); +} + +/* ---------------------------------------------------------------------- */ + +/* + * copyup the deleted file for writing. + */ +static int au_do_cpup_wh(struct au_cp_generic *cpg, struct dentry *wh_dentry, + struct file *file) +{ + int err; + unsigned int flags_orig; + aufs_bindex_t bsrc_orig; + struct au_dinfo *dinfo; + struct { + struct au_hdentry *hd; + struct dentry *h_dentry; + } hdst, hsrc; + + dinfo = au_di(cpg->dentry); + AuRwMustWriteLock(&dinfo->di_rwsem); + + bsrc_orig = cpg->bsrc; + cpg->bsrc = dinfo->di_btop; + hdst.hd = au_hdentry(dinfo, cpg->bdst); + hdst.h_dentry = hdst.hd->hd_dentry; + hdst.hd->hd_dentry = wh_dentry; + dinfo->di_btop = cpg->bdst; + + hsrc.h_dentry = NULL; + if (file) { + hsrc.hd = au_hdentry(dinfo, cpg->bsrc); + hsrc.h_dentry = hsrc.hd->hd_dentry; + hsrc.hd->hd_dentry = au_hf_top(file)->f_path.dentry; + } + flags_orig = cpg->flags; + cpg->flags = !AuCpup_DTIME; + err = au_cpup_single(cpg, /*h_parent*/NULL); + cpg->flags = flags_orig; + if (file) { + if (!err) + err = au_reopen_nondir(file); + hsrc.hd->hd_dentry = hsrc.h_dentry; + } + hdst.hd->hd_dentry = hdst.h_dentry; + dinfo->di_btop = cpg->bsrc; + cpg->bsrc = bsrc_orig; + + return err; +} + +static int au_cpup_wh(struct au_cp_generic *cpg, struct file *file) +{ + int err; + aufs_bindex_t bdst; + struct au_dtime dt; + struct dentry *dentry, *parent, *h_parent, *wh_dentry; + struct au_branch *br; + struct path h_path; + + dentry = cpg->dentry; + bdst = cpg->bdst; + br = au_sbr(dentry->d_sb, bdst); + parent = dget_parent(dentry); + h_parent = au_h_dptr(parent, bdst); + wh_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name); + err = PTR_ERR(wh_dentry); + if (IS_ERR(wh_dentry)) + goto out; + + h_path.dentry = h_parent; + h_path.mnt = au_br_mnt(br); + au_dtime_store(&dt, parent, &h_path); + err = au_do_cpup_wh(cpg, wh_dentry, file); + if (unlikely(err)) + goto out_wh; + + dget(wh_dentry); + h_path.dentry = wh_dentry; + if (!d_is_dir(wh_dentry)) { + /* no delegation since it is just created */ + err = vfsub_unlink(d_inode(h_parent), &h_path, + /*delegated*/NULL, /*force*/0); + } else + err = vfsub_rmdir(d_inode(h_parent), &h_path); + if (unlikely(err)) { + AuIOErr("failed remove copied-up tmp file %pd(%d)\n", + wh_dentry, err); + err = -EIO; + } + au_dtime_revert(&dt); + au_set_hi_wh(d_inode(dentry), bdst, wh_dentry); + +out_wh: + dput(wh_dentry); +out: + dput(parent); + return err; +} + +struct au_cpup_wh_args { + int *errp; + struct au_cp_generic *cpg; + struct file *file; +}; + +static void au_call_cpup_wh(void *args) +{ + struct au_cpup_wh_args *a = args; + + au_pin_hdir_acquire_nest(a->cpg->pin); + *a->errp = au_cpup_wh(a->cpg, a->file); + au_pin_hdir_release(a->cpg->pin); +} + +int au_sio_cpup_wh(struct au_cp_generic *cpg, struct file *file) +{ + int err, wkq_err; + aufs_bindex_t bdst; + struct dentry *dentry, *parent, *h_orph, *h_parent; + struct inode *dir, *h_dir, *h_tmpdir; + struct au_wbr *wbr; + struct au_pin wh_pin, *pin_orig; + + dentry = cpg->dentry; + bdst = cpg->bdst; + parent = dget_parent(dentry); + dir = d_inode(parent); + h_orph = NULL; + h_parent = NULL; + h_dir = au_igrab(au_h_iptr(dir, bdst)); + h_tmpdir = h_dir; + pin_orig = NULL; + if (!h_dir->i_nlink) { + wbr = au_sbr(dentry->d_sb, bdst)->br_wbr; + h_orph = wbr->wbr_orph; + + h_parent = dget(au_h_dptr(parent, bdst)); + au_set_h_dptr(parent, bdst, dget(h_orph)); + h_tmpdir = d_inode(h_orph); + au_set_h_iptr(dir, bdst, au_igrab(h_tmpdir), /*flags*/0); + + inode_lock_nested(h_tmpdir, AuLsc_I_PARENT3); + /* todo: au_h_open_pre()? */ + + pin_orig = cpg->pin; + au_pin_init(&wh_pin, dentry, bdst, AuLsc_DI_PARENT, + AuLsc_I_PARENT3, cpg->pin->udba, AuPin_DI_LOCKED); + cpg->pin = &wh_pin; + } + + if (!au_test_h_perm_sio(h_tmpdir, MAY_EXEC | MAY_WRITE) + && !au_cpup_sio_test(cpg->pin, d_inode(dentry)->i_mode)) + err = au_cpup_wh(cpg, file); + else { + struct au_cpup_wh_args args = { + .errp = &err, + .cpg = cpg, + .file = file + }; + wkq_err = au_wkq_wait(au_call_cpup_wh, &args); + if (unlikely(wkq_err)) + err = wkq_err; + } + + if (h_orph) { + inode_unlock(h_tmpdir); + /* todo: au_h_open_post()? */ + au_set_h_iptr(dir, bdst, au_igrab(h_dir), /*flags*/0); + au_set_h_dptr(parent, bdst, h_parent); + AuDebugOn(!pin_orig); + cpg->pin = pin_orig; + } + iput(h_dir); + dput(parent); + + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* + * generic routine for both of copy-up and copy-down. + */ +/* cf. revalidate function in file.c */ +int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, + int (*cp)(struct dentry *dentry, aufs_bindex_t bdst, + struct au_pin *pin, + struct dentry *h_parent, void *arg), + void *arg) +{ + int err; + struct au_pin pin; + struct dentry *d, *parent, *h_parent, *real_parent, *h_dentry; + + err = 0; + parent = dget_parent(dentry); + if (IS_ROOT(parent)) + goto out; + + au_pin_init(&pin, dentry, bdst, AuLsc_DI_PARENT2, AuLsc_I_PARENT2, + au_opt_udba(dentry->d_sb), AuPin_MNT_WRITE); + + /* do not use au_dpage */ + real_parent = parent; + while (1) { + dput(parent); + parent = dget_parent(dentry); + h_parent = au_h_dptr(parent, bdst); + if (h_parent) + goto out; /* success */ + + /* find top dir which is necessary to cpup */ + do { + d = parent; + dput(parent); + parent = dget_parent(d); + di_read_lock_parent3(parent, !AuLock_IR); + h_parent = au_h_dptr(parent, bdst); + di_read_unlock(parent, !AuLock_IR); + } while (!h_parent); + + if (d != real_parent) + di_write_lock_child3(d); + + /* somebody else might create while we were sleeping */ + h_dentry = au_h_dptr(d, bdst); + if (!h_dentry || d_is_negative(h_dentry)) { + if (h_dentry) + au_update_dbtop(d); + + au_pin_set_dentry(&pin, d); + err = au_do_pin(&pin); + if (!err) { + err = cp(d, bdst, &pin, h_parent, arg); + au_unpin(&pin); + } + } + + if (d != real_parent) + di_write_unlock(d); + if (unlikely(err)) + break; + } + +out: + dput(parent); + return err; +} + +static int au_cpup_dir(struct dentry *dentry, aufs_bindex_t bdst, + struct au_pin *pin, + struct dentry *h_parent __maybe_unused, + void *arg __maybe_unused) +{ + struct au_cp_generic cpg = { + .dentry = dentry, + .bdst = bdst, + .bsrc = -1, + .len = 0, + .pin = pin, + .flags = AuCpup_DTIME + }; + return au_sio_cpup_simple(&cpg); +} + +int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst) +{ + return au_cp_dirs(dentry, bdst, au_cpup_dir, NULL); +} + +int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst) +{ + int err; + struct dentry *parent; + struct inode *dir; + + parent = dget_parent(dentry); + dir = d_inode(parent); + err = 0; + if (au_h_iptr(dir, bdst)) + goto out; + + di_read_unlock(parent, AuLock_IR); + di_write_lock_parent(parent); + /* someone else might change our inode while we were sleeping */ + if (!au_h_iptr(dir, bdst)) + err = au_cpup_dirs(dentry, bdst); + di_downgrade_lock(parent, AuLock_IR); + +out: + dput(parent); + return err; +} diff --git a/fs/aufs/cpup.h b/fs/aufs/cpup.h new file mode 100644 index 0000000000000..a2cff31c04273 --- /dev/null +++ b/fs/aufs/cpup.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * copy-up/down functions + */ + +#ifndef __AUFS_CPUP_H__ +#define __AUFS_CPUP_H__ + +#ifdef __KERNEL__ + +#include + +struct inode; +struct file; +struct au_pin; + +void au_cpup_attr_flags(struct inode *dst, unsigned int iflags); +void au_cpup_attr_timesizes(struct inode *inode); +void au_cpup_attr_nlink(struct inode *inode, int force); +void au_cpup_attr_changeable(struct inode *inode); +void au_cpup_igen(struct inode *inode, struct inode *h_inode); +void au_cpup_attr_all(struct inode *inode, int force); + +/* ---------------------------------------------------------------------- */ + +struct au_cp_generic { + struct dentry *dentry; + aufs_bindex_t bdst, bsrc; + loff_t len; + struct au_pin *pin; + unsigned int flags; +}; + +/* cpup flags */ +#define AuCpup_DTIME 1 /* do dtime_store/revert */ +#define AuCpup_KEEPLINO (1 << 1) /* do not clear the lower xino, + for link(2) */ +#define AuCpup_RENAME (1 << 2) /* rename after cpup */ +#define AuCpup_HOPEN (1 << 3) /* call h_open_pre/post() in + cpup */ +#define AuCpup_OVERWRITE (1 << 4) /* allow overwriting the + existing entry */ +#define AuCpup_RWDST (1 << 5) /* force write target even if + the branch is marked as RO */ + +#ifndef CONFIG_AUFS_BR_HFSPLUS +#undef AuCpup_HOPEN +#define AuCpup_HOPEN 0 +#endif + +#define au_ftest_cpup(flags, name) ((flags) & AuCpup_##name) +#define au_fset_cpup(flags, name) \ + do { (flags) |= AuCpup_##name; } while (0) +#define au_fclr_cpup(flags, name) \ + do { (flags) &= ~AuCpup_##name; } while (0) + +int au_copy_file(struct file *dst, struct file *src, loff_t len); +int au_sio_cpup_simple(struct au_cp_generic *cpg); +int au_sio_cpdown_simple(struct au_cp_generic *cpg); +int au_sio_cpup_wh(struct au_cp_generic *cpg, struct file *file); + +int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, + int (*cp)(struct dentry *dentry, aufs_bindex_t bdst, + struct au_pin *pin, + struct dentry *h_parent, void *arg), + void *arg); +int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst); +int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst); + +/* ---------------------------------------------------------------------- */ + +/* keep timestamps when copyup */ +struct au_dtime { + struct dentry *dt_dentry; + struct path dt_h_path; + struct timespec64 dt_atime, dt_mtime; +}; +void au_dtime_store(struct au_dtime *dt, struct dentry *dentry, + struct path *h_path); +void au_dtime_revert(struct au_dtime *dt); + +#endif /* __KERNEL__ */ +#endif /* __AUFS_CPUP_H__ */ diff --git a/fs/aufs/dbgaufs.c b/fs/aufs/dbgaufs.c new file mode 100644 index 0000000000000..7df6d89242f61 --- /dev/null +++ b/fs/aufs/dbgaufs.c @@ -0,0 +1,526 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * debugfs interface + */ + +#include +#include "aufs.h" + +#ifndef CONFIG_SYSFS +#error DEBUG_FS depends upon SYSFS +#endif + +static struct dentry *dbgaufs; +static const mode_t dbgaufs_mode = 0444; + +/* 20 is max digits length of ulong 64 */ +struct dbgaufs_arg { + int n; + char a[20 * 4]; +}; + +/* + * common function for all XINO files + */ +static int dbgaufs_xi_release(struct inode *inode __maybe_unused, + struct file *file) +{ + void *p; + + p = file->private_data; + if (p) { + /* this is struct dbgaufs_arg */ + AuDebugOn(!au_kfree_sz_test(p)); + au_kfree_do_rcu(p); + } + return 0; +} + +static int dbgaufs_xi_open(struct file *xf, struct file *file, int do_fcnt, + int cnt) +{ + int err; + struct kstat st; + struct dbgaufs_arg *p; + + err = -ENOMEM; + p = kmalloc(sizeof(*p), GFP_NOFS); + if (unlikely(!p)) + goto out; + + err = 0; + p->n = 0; + file->private_data = p; + if (!xf) + goto out; + + err = vfsub_getattr(&xf->f_path, &st); + if (!err) { + if (do_fcnt) + p->n = snprintf + (p->a, sizeof(p->a), "%d, %llux%u %lld\n", + cnt, st.blocks, st.blksize, + (long long)st.size); + else + p->n = snprintf(p->a, sizeof(p->a), "%llux%u %lld\n", + st.blocks, st.blksize, + (long long)st.size); + AuDebugOn(p->n >= sizeof(p->a)); + } else { + p->n = snprintf(p->a, sizeof(p->a), "err %d\n", err); + err = 0; + } + +out: + return err; +} + +static ssize_t dbgaufs_xi_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct dbgaufs_arg *p; + + p = file->private_data; + return simple_read_from_buffer(buf, count, ppos, p->a, p->n); +} + +/* ---------------------------------------------------------------------- */ + +struct dbgaufs_plink_arg { + int n; + char a[]; +}; + +static int dbgaufs_plink_release(struct inode *inode __maybe_unused, + struct file *file) +{ + free_page((unsigned long)file->private_data); + return 0; +} + +static int dbgaufs_plink_open(struct inode *inode, struct file *file) +{ + int err, i, limit; + unsigned long n, sum; + struct dbgaufs_plink_arg *p; + struct au_sbinfo *sbinfo; + struct super_block *sb; + struct hlist_bl_head *hbl; + + err = -ENOMEM; + p = (void *)get_zeroed_page(GFP_NOFS); + if (unlikely(!p)) + goto out; + + err = -EFBIG; + sbinfo = inode->i_private; + sb = sbinfo->si_sb; + si_noflush_read_lock(sb); + if (au_opt_test(au_mntflags(sb), PLINK)) { + limit = PAGE_SIZE - sizeof(p->n); + + /* the number of buckets */ + n = snprintf(p->a + p->n, limit, "%d\n", AuPlink_NHASH); + p->n += n; + limit -= n; + + sum = 0; + for (i = 0, hbl = sbinfo->si_plink; i < AuPlink_NHASH; + i++, hbl++) { + n = au_hbl_count(hbl); + sum += n; + + n = snprintf(p->a + p->n, limit, "%lu ", n); + p->n += n; + limit -= n; + if (unlikely(limit <= 0)) + goto out_free; + } + p->a[p->n - 1] = '\n'; + + /* the sum of plinks */ + n = snprintf(p->a + p->n, limit, "%lu\n", sum); + p->n += n; + limit -= n; + if (unlikely(limit <= 0)) + goto out_free; + } else { +#define str "1\n0\n0\n" + p->n = sizeof(str) - 1; + strcpy(p->a, str); +#undef str + } + si_read_unlock(sb); + + err = 0; + file->private_data = p; + goto out; /* success */ + +out_free: + free_page((unsigned long)p); +out: + return err; +} + +static ssize_t dbgaufs_plink_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct dbgaufs_plink_arg *p; + + p = file->private_data; + return simple_read_from_buffer(buf, count, ppos, p->a, p->n); +} + +static const struct file_operations dbgaufs_plink_fop = { + .owner = THIS_MODULE, + .open = dbgaufs_plink_open, + .release = dbgaufs_plink_release, + .read = dbgaufs_plink_read +}; + +/* ---------------------------------------------------------------------- */ + +static int dbgaufs_xib_open(struct inode *inode, struct file *file) +{ + int err; + struct au_sbinfo *sbinfo; + struct super_block *sb; + + sbinfo = inode->i_private; + sb = sbinfo->si_sb; + si_noflush_read_lock(sb); + err = dbgaufs_xi_open(sbinfo->si_xib, file, /*do_fcnt*/0, /*cnt*/0); + si_read_unlock(sb); + return err; +} + +static const struct file_operations dbgaufs_xib_fop = { + .owner = THIS_MODULE, + .open = dbgaufs_xib_open, + .release = dbgaufs_xi_release, + .read = dbgaufs_xi_read +}; + +/* ---------------------------------------------------------------------- */ + +#define DbgaufsXi_PREFIX "xi" + +static int dbgaufs_xino_open(struct inode *inode, struct file *file) +{ + int err, idx; + long l; + aufs_bindex_t bindex; + char *p, a[sizeof(DbgaufsXi_PREFIX) + 8]; + struct au_sbinfo *sbinfo; + struct super_block *sb; + struct au_xino *xi; + struct file *xf; + struct qstr *name; + struct au_branch *br; + + err = -ENOENT; + name = &file->f_path.dentry->d_name; + if (unlikely(name->len < sizeof(DbgaufsXi_PREFIX) + || memcmp(name->name, DbgaufsXi_PREFIX, + sizeof(DbgaufsXi_PREFIX) - 1))) + goto out; + + AuDebugOn(name->len >= sizeof(a)); + memcpy(a, name->name, name->len); + a[name->len] = '\0'; + p = strchr(a, '-'); + if (p) + *p = '\0'; + err = kstrtol(a + sizeof(DbgaufsXi_PREFIX) - 1, 10, &l); + if (unlikely(err)) + goto out; + bindex = l; + idx = 0; + if (p) { + err = kstrtol(p + 1, 10, &l); + if (unlikely(err)) + goto out; + idx = l; + } + + err = -ENOENT; + sbinfo = inode->i_private; + sb = sbinfo->si_sb; + si_noflush_read_lock(sb); + if (unlikely(bindex < 0 || bindex > au_sbbot(sb))) + goto out_si; + br = au_sbr(sb, bindex); + xi = br->br_xino; + if (unlikely(idx >= xi->xi_nfile)) + goto out_si; + xf = au_xino_file(xi, idx); + if (xf) + err = dbgaufs_xi_open(xf, file, /*do_fcnt*/1, + au_xino_count(br)); + +out_si: + si_read_unlock(sb); +out: + AuTraceErr(err); + return err; +} + +static const struct file_operations dbgaufs_xino_fop = { + .owner = THIS_MODULE, + .open = dbgaufs_xino_open, + .release = dbgaufs_xi_release, + .read = dbgaufs_xi_read +}; + +void dbgaufs_xino_del(struct au_branch *br) +{ + struct dentry *dbgaufs; + + dbgaufs = br->br_dbgaufs; + if (!dbgaufs) + return; + + br->br_dbgaufs = NULL; + /* debugfs acquires the parent i_mutex */ + lockdep_off(); + debugfs_remove(dbgaufs); + lockdep_on(); +} + +void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) +{ + aufs_bindex_t bbot; + struct au_branch *br; + + if (!au_sbi(sb)->si_dbgaufs) + return; + + bbot = au_sbbot(sb); + for (; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + dbgaufs_xino_del(br); + } +} + +static void dbgaufs_br_do_add(struct super_block *sb, aufs_bindex_t bindex, + unsigned int idx, struct dentry *parent, + struct au_sbinfo *sbinfo) +{ + struct au_branch *br; + struct dentry *d; + /* "xi" bindex(5) "-" idx(2) NULL */ + char name[sizeof(DbgaufsXi_PREFIX) + 8]; + + if (!idx) + snprintf(name, sizeof(name), DbgaufsXi_PREFIX "%d", bindex); + else + snprintf(name, sizeof(name), DbgaufsXi_PREFIX "%d-%u", + bindex, idx); + br = au_sbr(sb, bindex); + if (br->br_dbgaufs) { + struct qstr qstr = QSTR_INIT(name, strlen(name)); + + if (!au_qstreq(&br->br_dbgaufs->d_name, &qstr)) { + /* debugfs acquires the parent i_mutex */ + lockdep_off(); + d = debugfs_rename(parent, br->br_dbgaufs, parent, + name); + lockdep_on(); + if (unlikely(!d)) + pr_warn("failed renaming %pd/%s, ignored.\n", + parent, name); + } + } else { + lockdep_off(); + br->br_dbgaufs = debugfs_create_file(name, dbgaufs_mode, parent, + sbinfo, &dbgaufs_xino_fop); + lockdep_on(); + if (unlikely(!br->br_dbgaufs)) + pr_warn("failed creating %pd/%s, ignored.\n", + parent, name); + } +} + +static void dbgaufs_br_add(struct super_block *sb, aufs_bindex_t bindex, + struct dentry *parent, struct au_sbinfo *sbinfo) +{ + struct au_branch *br; + struct au_xino *xi; + unsigned int u; + + br = au_sbr(sb, bindex); + xi = br->br_xino; + for (u = 0; u < xi->xi_nfile; u++) + dbgaufs_br_do_add(sb, bindex, u, parent, sbinfo); +} + +void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex, int topdown) +{ + struct au_sbinfo *sbinfo; + struct dentry *parent; + aufs_bindex_t bbot; + + if (!au_opt_test(au_mntflags(sb), XINO)) + return; + + sbinfo = au_sbi(sb); + parent = sbinfo->si_dbgaufs; + if (!parent) + return; + + bbot = au_sbbot(sb); + if (topdown) + for (; bindex <= bbot; bindex++) + dbgaufs_br_add(sb, bindex, parent, sbinfo); + else + for (; bbot >= bindex; bbot--) + dbgaufs_br_add(sb, bbot, parent, sbinfo); +} + +/* ---------------------------------------------------------------------- */ + +#ifdef CONFIG_AUFS_EXPORT +static int dbgaufs_xigen_open(struct inode *inode, struct file *file) +{ + int err; + struct au_sbinfo *sbinfo; + struct super_block *sb; + + sbinfo = inode->i_private; + sb = sbinfo->si_sb; + si_noflush_read_lock(sb); + err = dbgaufs_xi_open(sbinfo->si_xigen, file, /*do_fcnt*/0, /*cnt*/0); + si_read_unlock(sb); + return err; +} + +static const struct file_operations dbgaufs_xigen_fop = { + .owner = THIS_MODULE, + .open = dbgaufs_xigen_open, + .release = dbgaufs_xi_release, + .read = dbgaufs_xi_read +}; + +static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo) +{ + int err; + + /* + * This function is a dynamic '__init' function actually, + * so the tiny check for si_rwsem is unnecessary. + */ + /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ + + err = -EIO; + sbinfo->si_dbgaufs_xigen = debugfs_create_file + ("xigen", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, + &dbgaufs_xigen_fop); + if (sbinfo->si_dbgaufs_xigen) + err = 0; + + return err; +} +#else +static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo) +{ + return 0; +} +#endif /* CONFIG_AUFS_EXPORT */ + +/* ---------------------------------------------------------------------- */ + +void dbgaufs_si_fin(struct au_sbinfo *sbinfo) +{ + /* + * This function is a dynamic '__fin' function actually, + * so the tiny check for si_rwsem is unnecessary. + */ + /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ + + debugfs_remove_recursive(sbinfo->si_dbgaufs); + sbinfo->si_dbgaufs = NULL; +} + +int dbgaufs_si_init(struct au_sbinfo *sbinfo) +{ + int err; + char name[SysaufsSiNameLen]; + + /* + * This function is a dynamic '__init' function actually, + * so the tiny check for si_rwsem is unnecessary. + */ + /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ + + err = -ENOENT; + if (!dbgaufs) { + AuErr1("/debug/aufs is uninitialized\n"); + goto out; + } + + err = -EIO; + sysaufs_name(sbinfo, name); + sbinfo->si_dbgaufs = debugfs_create_dir(name, dbgaufs); + if (unlikely(!sbinfo->si_dbgaufs)) + goto out; + + /* regardless plink/noplink option */ + sbinfo->si_dbgaufs_plink = debugfs_create_file + ("plink", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, + &dbgaufs_plink_fop); + if (unlikely(!sbinfo->si_dbgaufs_plink)) + goto out_dir; + + /* regardless xino/noxino option */ + sbinfo->si_dbgaufs_xib = debugfs_create_file + ("xib", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, + &dbgaufs_xib_fop); + if (unlikely(!sbinfo->si_dbgaufs_xib)) + goto out_dir; + + err = dbgaufs_xigen_init(sbinfo); + if (!err) + goto out; /* success */ + +out_dir: + dbgaufs_si_fin(sbinfo); +out: + if (unlikely(err)) + pr_err("debugfs/aufs failed\n"); + return err; +} + +/* ---------------------------------------------------------------------- */ + +void dbgaufs_fin(void) +{ + debugfs_remove(dbgaufs); +} + +int __init dbgaufs_init(void) +{ + int err; + + err = -EIO; + dbgaufs = debugfs_create_dir(AUFS_NAME, NULL); + if (dbgaufs) + err = 0; + return err; +} diff --git a/fs/aufs/dbgaufs.h b/fs/aufs/dbgaufs.h new file mode 100644 index 0000000000000..8e0567c72ec87 --- /dev/null +++ b/fs/aufs/dbgaufs.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * debugfs interface + */ + +#ifndef __DBGAUFS_H__ +#define __DBGAUFS_H__ + +#ifdef __KERNEL__ + +struct super_block; +struct au_sbinfo; +struct au_branch; + +#ifdef CONFIG_DEBUG_FS +/* dbgaufs.c */ +void dbgaufs_xino_del(struct au_branch *br); +void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex); +void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex, int topdown); +void dbgaufs_si_fin(struct au_sbinfo *sbinfo); +int dbgaufs_si_init(struct au_sbinfo *sbinfo); +void dbgaufs_fin(void); +int __init dbgaufs_init(void); +#else +AuStubVoid(dbgaufs_xino_del, struct au_branch *br) +AuStubVoid(dbgaufs_brs_del, struct super_block *sb, aufs_bindex_t bindex) +AuStubVoid(dbgaufs_brs_add, struct super_block *sb, aufs_bindex_t bindex, + int topdown) +AuStubVoid(dbgaufs_si_fin, struct au_sbinfo *sbinfo) +AuStubInt0(dbgaufs_si_init, struct au_sbinfo *sbinfo) +AuStubVoid(dbgaufs_fin, void) +AuStubInt0(__init dbgaufs_init, void) +#endif /* CONFIG_DEBUG_FS */ + +#endif /* __KERNEL__ */ +#endif /* __DBGAUFS_H__ */ diff --git a/fs/aufs/dcsub.c b/fs/aufs/dcsub.c new file mode 100644 index 0000000000000..0da952034aea1 --- /dev/null +++ b/fs/aufs/dcsub.c @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * sub-routines for dentry cache + */ + +#include "aufs.h" + +static void au_dpage_free(struct au_dpage *dpage) +{ + int i; + struct dentry **p; + + p = dpage->dentries; + for (i = 0; i < dpage->ndentry; i++) + dput(*p++); + free_page((unsigned long)dpage->dentries); +} + +int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp) +{ + int err; + void *p; + + err = -ENOMEM; + dpages->dpages = kmalloc(sizeof(*dpages->dpages), gfp); + if (unlikely(!dpages->dpages)) + goto out; + + p = (void *)__get_free_page(gfp); + if (unlikely(!p)) + goto out_dpages; + + dpages->dpages[0].ndentry = 0; + dpages->dpages[0].dentries = p; + dpages->ndpage = 1; + return 0; /* success */ + +out_dpages: + au_kfree_try_rcu(dpages->dpages); +out: + return err; +} + +void au_dpages_free(struct au_dcsub_pages *dpages) +{ + int i; + struct au_dpage *p; + + p = dpages->dpages; + for (i = 0; i < dpages->ndpage; i++) + au_dpage_free(p++); + au_kfree_try_rcu(dpages->dpages); +} + +static int au_dpages_append(struct au_dcsub_pages *dpages, + struct dentry *dentry, gfp_t gfp) +{ + int err, sz; + struct au_dpage *dpage; + void *p; + + dpage = dpages->dpages + dpages->ndpage - 1; + sz = PAGE_SIZE / sizeof(dentry); + if (unlikely(dpage->ndentry >= sz)) { + AuLabel(new dpage); + err = -ENOMEM; + sz = dpages->ndpage * sizeof(*dpages->dpages); + p = au_kzrealloc(dpages->dpages, sz, + sz + sizeof(*dpages->dpages), gfp, + /*may_shrink*/0); + if (unlikely(!p)) + goto out; + + dpages->dpages = p; + dpage = dpages->dpages + dpages->ndpage; + p = (void *)__get_free_page(gfp); + if (unlikely(!p)) + goto out; + + dpage->ndentry = 0; + dpage->dentries = p; + dpages->ndpage++; + } + + AuDebugOn(au_dcount(dentry) <= 0); + dpage->dentries[dpage->ndentry++] = dget_dlock(dentry); + return 0; /* success */ + +out: + return err; +} + +/* todo: BAD approach */ +/* copied from linux/fs/dcache.c */ +enum d_walk_ret { + D_WALK_CONTINUE, + D_WALK_QUIT, + D_WALK_NORETRY, + D_WALK_SKIP, +}; + +extern void d_walk(struct dentry *parent, void *data, + enum d_walk_ret (*enter)(void *, struct dentry *)); + +struct ac_dpages_arg { + int err; + struct au_dcsub_pages *dpages; + struct super_block *sb; + au_dpages_test test; + void *arg; +}; + +static enum d_walk_ret au_call_dpages_append(void *_arg, struct dentry *dentry) +{ + enum d_walk_ret ret; + struct ac_dpages_arg *arg = _arg; + + ret = D_WALK_CONTINUE; + if (dentry->d_sb == arg->sb + && !IS_ROOT(dentry) + && au_dcount(dentry) > 0 + && au_di(dentry) + && (!arg->test || arg->test(dentry, arg->arg))) { + arg->err = au_dpages_append(arg->dpages, dentry, GFP_ATOMIC); + if (unlikely(arg->err)) + ret = D_WALK_QUIT; + } + + return ret; +} + +int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root, + au_dpages_test test, void *arg) +{ + struct ac_dpages_arg args = { + .err = 0, + .dpages = dpages, + .sb = root->d_sb, + .test = test, + .arg = arg + }; + + d_walk(root, &args, au_call_dpages_append); + + return args.err; +} + +int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry, + int do_include, au_dpages_test test, void *arg) +{ + int err; + + err = 0; + write_seqlock(&rename_lock); + spin_lock(&dentry->d_lock); + if (do_include + && au_dcount(dentry) > 0 + && (!test || test(dentry, arg))) + err = au_dpages_append(dpages, dentry, GFP_ATOMIC); + spin_unlock(&dentry->d_lock); + if (unlikely(err)) + goto out; + + /* + * RCU for vfsmount is unnecessary since this is a traverse in a single + * mount + */ + while (!IS_ROOT(dentry)) { + dentry = dentry->d_parent; /* rename_lock is locked */ + spin_lock(&dentry->d_lock); + if (au_dcount(dentry) > 0 + && (!test || test(dentry, arg))) + err = au_dpages_append(dpages, dentry, GFP_ATOMIC); + spin_unlock(&dentry->d_lock); + if (unlikely(err)) + break; + } + +out: + write_sequnlock(&rename_lock); + return err; +} + +static inline int au_dcsub_dpages_aufs(struct dentry *dentry, void *arg) +{ + return au_di(dentry) && dentry->d_sb == arg; +} + +int au_dcsub_pages_rev_aufs(struct au_dcsub_pages *dpages, + struct dentry *dentry, int do_include) +{ + return au_dcsub_pages_rev(dpages, dentry, do_include, + au_dcsub_dpages_aufs, dentry->d_sb); +} + +int au_test_subdir(struct dentry *d1, struct dentry *d2) +{ + struct path path[2] = { + { + .dentry = d1 + }, + { + .dentry = d2 + } + }; + + return path_is_under(path + 0, path + 1); +} diff --git a/fs/aufs/dcsub.h b/fs/aufs/dcsub.h new file mode 100644 index 0000000000000..1fde5f6aec5cd --- /dev/null +++ b/fs/aufs/dcsub.h @@ -0,0 +1,137 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * sub-routines for dentry cache + */ + +#ifndef __AUFS_DCSUB_H__ +#define __AUFS_DCSUB_H__ + +#ifdef __KERNEL__ + +#include +#include + +struct au_dpage { + int ndentry; + struct dentry **dentries; +}; + +struct au_dcsub_pages { + int ndpage; + struct au_dpage *dpages; +}; + +/* ---------------------------------------------------------------------- */ + +/* dcsub.c */ +int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp); +void au_dpages_free(struct au_dcsub_pages *dpages); +typedef int (*au_dpages_test)(struct dentry *dentry, void *arg); +int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root, + au_dpages_test test, void *arg); +int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry, + int do_include, au_dpages_test test, void *arg); +int au_dcsub_pages_rev_aufs(struct au_dcsub_pages *dpages, + struct dentry *dentry, int do_include); +int au_test_subdir(struct dentry *d1, struct dentry *d2); + +/* ---------------------------------------------------------------------- */ + +/* + * todo: in linux-3.13, several similar (but faster) helpers are added to + * include/linux/dcache.h. Try them (in the future). + */ + +static inline int au_d_hashed_positive(struct dentry *d) +{ + int err; + struct inode *inode = d_inode(d); + + err = 0; + if (unlikely(d_unhashed(d) + || d_is_negative(d) + || !inode->i_nlink)) + err = -ENOENT; + return err; +} + +static inline int au_d_linkable(struct dentry *d) +{ + int err; + struct inode *inode = d_inode(d); + + err = au_d_hashed_positive(d); + if (err + && d_is_positive(d) + && (inode->i_state & I_LINKABLE)) + err = 0; + return err; +} + +static inline int au_d_alive(struct dentry *d) +{ + int err; + struct inode *inode; + + err = 0; + if (!IS_ROOT(d)) + err = au_d_hashed_positive(d); + else { + inode = d_inode(d); + if (unlikely(d_unlinked(d) + || d_is_negative(d) + || !inode->i_nlink)) + err = -ENOENT; + } + return err; +} + +static inline int au_alive_dir(struct dentry *d) +{ + int err; + + err = au_d_alive(d); + if (unlikely(err || IS_DEADDIR(d_inode(d)))) + err = -ENOENT; + return err; +} + +static inline int au_qstreq(struct qstr *a, struct qstr *b) +{ + return a->len == b->len + && !memcmp(a->name, b->name, a->len); +} + +/* + * by the commit + * 360f547 2015-01-25 dcache: let the dentry count go down to zero without + * taking d_lock + * the type of d_lockref.count became int, but the inlined function d_count() + * still returns unsigned int. + * I don't know why. Maybe it is for every d_count() users? + * Anyway au_dcount() lives on. + */ +static inline int au_dcount(struct dentry *d) +{ + return (int)d_count(d); +} + +#endif /* __KERNEL__ */ +#endif /* __AUFS_DCSUB_H__ */ diff --git a/fs/aufs/debug.c b/fs/aufs/debug.c new file mode 100644 index 0000000000000..9765ce3eeeef3 --- /dev/null +++ b/fs/aufs/debug.c @@ -0,0 +1,444 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * debug print functions + */ + +#include +#include "aufs.h" + +/* Returns 0, or -errno. arg is in kp->arg. */ +static int param_atomic_t_set(const char *val, const struct kernel_param *kp) +{ + int err, n; + + err = kstrtoint(val, 0, &n); + if (!err) { + if (n > 0) + au_debug_on(); + else + au_debug_off(); + } + return err; +} + +/* Returns length written or -errno. Buffer is 4k (ie. be short!) */ +static int param_atomic_t_get(char *buffer, const struct kernel_param *kp) +{ + atomic_t *a; + + a = kp->arg; + return sprintf(buffer, "%d", atomic_read(a)); +} + +static const struct kernel_param_ops param_ops_atomic_t = { + .set = param_atomic_t_set, + .get = param_atomic_t_get + /* void (*free)(void *arg) */ +}; + +atomic_t aufs_debug = ATOMIC_INIT(0); +MODULE_PARM_DESC(debug, "debug print"); +module_param_named(debug, aufs_debug, atomic_t, 0664); + +DEFINE_MUTEX(au_dbg_mtx); /* just to serialize the dbg msgs */ +char *au_plevel = KERN_DEBUG; +#define dpri(fmt, ...) do { \ + if ((au_plevel \ + && strcmp(au_plevel, KERN_DEBUG)) \ + || au_debug_test()) \ + printk("%s" fmt, au_plevel, ##__VA_ARGS__); \ +} while (0) + +/* ---------------------------------------------------------------------- */ + +void au_dpri_whlist(struct au_nhash *whlist) +{ + unsigned long ul, n; + struct hlist_head *head; + struct au_vdir_wh *pos; + + n = whlist->nh_num; + head = whlist->nh_head; + for (ul = 0; ul < n; ul++) { + hlist_for_each_entry(pos, head, wh_hash) + dpri("b%d, %.*s, %d\n", + pos->wh_bindex, + pos->wh_str.len, pos->wh_str.name, + pos->wh_str.len); + head++; + } +} + +void au_dpri_vdir(struct au_vdir *vdir) +{ + unsigned long ul; + union au_vdir_deblk_p p; + unsigned char *o; + + if (!vdir || IS_ERR(vdir)) { + dpri("err %ld\n", PTR_ERR(vdir)); + return; + } + + dpri("deblk %u, nblk %lu, deblk %p, last{%lu, %p}, ver %llu\n", + vdir->vd_deblk_sz, vdir->vd_nblk, vdir->vd_deblk, + vdir->vd_last.ul, vdir->vd_last.p.deblk, vdir->vd_version); + for (ul = 0; ul < vdir->vd_nblk; ul++) { + p.deblk = vdir->vd_deblk[ul]; + o = p.deblk; + dpri("[%lu]: %p\n", ul, o); + } +} + +static int do_pri_inode(aufs_bindex_t bindex, struct inode *inode, int hn, + struct dentry *wh) +{ + char *n = NULL; + int l = 0; + + if (!inode || IS_ERR(inode)) { + dpri("i%d: err %ld\n", bindex, PTR_ERR(inode)); + return -1; + } + + /* the type of i_blocks depends upon CONFIG_LBDAF */ + BUILD_BUG_ON(sizeof(inode->i_blocks) != sizeof(unsigned long) + && sizeof(inode->i_blocks) != sizeof(u64)); + if (wh) { + n = (void *)wh->d_name.name; + l = wh->d_name.len; + } + + dpri("i%d: %p, i%lu, %s, cnt %d, nl %u, 0%o, sz %llu, blk %llu," + " hn %d, ct %lld, np %lu, st 0x%lx, f 0x%x, v %llu, g %x%s%.*s\n", + bindex, inode, + inode->i_ino, inode->i_sb ? au_sbtype(inode->i_sb) : "??", + atomic_read(&inode->i_count), inode->i_nlink, inode->i_mode, + i_size_read(inode), (unsigned long long)inode->i_blocks, + hn, (long long)timespec64_to_ns(&inode->i_ctime) & 0x0ffff, + inode->i_mapping ? inode->i_mapping->nrpages : 0, + inode->i_state, inode->i_flags, inode_peek_iversion(inode), + inode->i_generation, + l ? ", wh " : "", l, n); + return 0; +} + +void au_dpri_inode(struct inode *inode) +{ + struct au_iinfo *iinfo; + struct au_hinode *hi; + aufs_bindex_t bindex; + int err, hn; + + err = do_pri_inode(-1, inode, -1, NULL); + if (err || !au_test_aufs(inode->i_sb) || au_is_bad_inode(inode)) + return; + + iinfo = au_ii(inode); + dpri("i-1: btop %d, bbot %d, gen %d\n", + iinfo->ii_btop, iinfo->ii_bbot, au_iigen(inode, NULL)); + if (iinfo->ii_btop < 0) + return; + hn = 0; + for (bindex = iinfo->ii_btop; bindex <= iinfo->ii_bbot; bindex++) { + hi = au_hinode(iinfo, bindex); + hn = !!au_hn(hi); + do_pri_inode(bindex, hi->hi_inode, hn, hi->hi_whdentry); + } +} + +void au_dpri_dalias(struct inode *inode) +{ + struct dentry *d; + + spin_lock(&inode->i_lock); + hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) + au_dpri_dentry(d); + spin_unlock(&inode->i_lock); +} + +static int do_pri_dentry(aufs_bindex_t bindex, struct dentry *dentry) +{ + struct dentry *wh = NULL; + int hn; + struct inode *inode; + struct au_iinfo *iinfo; + struct au_hinode *hi; + + if (!dentry || IS_ERR(dentry)) { + dpri("d%d: err %ld\n", bindex, PTR_ERR(dentry)); + return -1; + } + /* do not call dget_parent() here */ + /* note: access d_xxx without d_lock */ + dpri("d%d: %p, %pd2?, %s, cnt %d, flags 0x%x, %shashed\n", + bindex, dentry, dentry, + dentry->d_sb ? au_sbtype(dentry->d_sb) : "??", + au_dcount(dentry), dentry->d_flags, + d_unhashed(dentry) ? "un" : ""); + hn = -1; + inode = NULL; + if (d_is_positive(dentry)) + inode = d_inode(dentry); + if (inode + && au_test_aufs(dentry->d_sb) + && bindex >= 0 + && !au_is_bad_inode(inode)) { + iinfo = au_ii(inode); + hi = au_hinode(iinfo, bindex); + hn = !!au_hn(hi); + wh = hi->hi_whdentry; + } + do_pri_inode(bindex, inode, hn, wh); + return 0; +} + +void au_dpri_dentry(struct dentry *dentry) +{ + struct au_dinfo *dinfo; + aufs_bindex_t bindex; + int err; + + err = do_pri_dentry(-1, dentry); + if (err || !au_test_aufs(dentry->d_sb)) + return; + + dinfo = au_di(dentry); + if (!dinfo) + return; + dpri("d-1: btop %d, bbot %d, bwh %d, bdiropq %d, gen %d, tmp %d\n", + dinfo->di_btop, dinfo->di_bbot, + dinfo->di_bwh, dinfo->di_bdiropq, au_digen(dentry), + dinfo->di_tmpfile); + if (dinfo->di_btop < 0) + return; + for (bindex = dinfo->di_btop; bindex <= dinfo->di_bbot; bindex++) + do_pri_dentry(bindex, au_hdentry(dinfo, bindex)->hd_dentry); +} + +static int do_pri_file(aufs_bindex_t bindex, struct file *file) +{ + char a[32]; + + if (!file || IS_ERR(file)) { + dpri("f%d: err %ld\n", bindex, PTR_ERR(file)); + return -1; + } + a[0] = 0; + if (bindex < 0 + && !IS_ERR_OR_NULL(file->f_path.dentry) + && au_test_aufs(file->f_path.dentry->d_sb) + && au_fi(file)) + snprintf(a, sizeof(a), ", gen %d, mmapped %d", + au_figen(file), atomic_read(&au_fi(file)->fi_mmapped)); + dpri("f%d: mode 0x%x, flags 0%o, cnt %ld, v %llu, pos %llu%s\n", + bindex, file->f_mode, file->f_flags, (long)file_count(file), + file->f_version, file->f_pos, a); + if (!IS_ERR_OR_NULL(file->f_path.dentry)) + do_pri_dentry(bindex, file->f_path.dentry); + return 0; +} + +void au_dpri_file(struct file *file) +{ + struct au_finfo *finfo; + struct au_fidir *fidir; + struct au_hfile *hfile; + aufs_bindex_t bindex; + int err; + + err = do_pri_file(-1, file); + if (err + || IS_ERR_OR_NULL(file->f_path.dentry) + || !au_test_aufs(file->f_path.dentry->d_sb)) + return; + + finfo = au_fi(file); + if (!finfo) + return; + if (finfo->fi_btop < 0) + return; + fidir = finfo->fi_hdir; + if (!fidir) + do_pri_file(finfo->fi_btop, finfo->fi_htop.hf_file); + else + for (bindex = finfo->fi_btop; + bindex >= 0 && bindex <= fidir->fd_bbot; + bindex++) { + hfile = fidir->fd_hfile + bindex; + do_pri_file(bindex, hfile ? hfile->hf_file : NULL); + } +} + +static int do_pri_br(aufs_bindex_t bindex, struct au_branch *br) +{ + struct vfsmount *mnt; + struct super_block *sb; + + if (!br || IS_ERR(br)) + goto out; + mnt = au_br_mnt(br); + if (!mnt || IS_ERR(mnt)) + goto out; + sb = mnt->mnt_sb; + if (!sb || IS_ERR(sb)) + goto out; + + dpri("s%d: {perm 0x%x, id %d, wbr %p}, " + "%s, dev 0x%02x%02x, flags 0x%lx, cnt %d, active %d, " + "xino %d\n", + bindex, br->br_perm, br->br_id, br->br_wbr, + au_sbtype(sb), MAJOR(sb->s_dev), MINOR(sb->s_dev), + sb->s_flags, sb->s_count, + atomic_read(&sb->s_active), + !!au_xino_file(br->br_xino, /*idx*/-1)); + return 0; + +out: + dpri("s%d: err %ld\n", bindex, PTR_ERR(br)); + return -1; +} + +void au_dpri_sb(struct super_block *sb) +{ + struct au_sbinfo *sbinfo; + aufs_bindex_t bindex; + int err; + /* to reduce stack size */ + struct { + struct vfsmount mnt; + struct au_branch fake; + } *a; + + /* this function can be called from magic sysrq */ + a = kzalloc(sizeof(*a), GFP_ATOMIC); + if (unlikely(!a)) { + dpri("no memory\n"); + return; + } + + a->mnt.mnt_sb = sb; + a->fake.br_path.mnt = &a->mnt; + err = do_pri_br(-1, &a->fake); + au_kfree_rcu(a); + dpri("dev 0x%x\n", sb->s_dev); + if (err || !au_test_aufs(sb)) + return; + + sbinfo = au_sbi(sb); + if (!sbinfo) + return; + dpri("nw %d, gen %u, kobj %d\n", + atomic_read(&sbinfo->si_nowait.nw_len), sbinfo->si_generation, + kref_read(&sbinfo->si_kobj.kref)); + for (bindex = 0; bindex <= sbinfo->si_bbot; bindex++) + do_pri_br(bindex, sbinfo->si_branch[0 + bindex]); +} + +/* ---------------------------------------------------------------------- */ + +void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line) +{ + struct inode *h_inode, *inode = d_inode(dentry); + struct dentry *h_dentry; + aufs_bindex_t bindex, bbot, bi; + + if (!inode /* || au_di(dentry)->di_lsc == AuLsc_DI_TMP */) + return; + + bbot = au_dbbot(dentry); + bi = au_ibbot(inode); + if (bi < bbot) + bbot = bi; + bindex = au_dbtop(dentry); + bi = au_ibtop(inode); + if (bi > bindex) + bindex = bi; + + for (; bindex <= bbot; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; + h_inode = au_h_iptr(inode, bindex); + if (unlikely(h_inode != d_inode(h_dentry))) { + au_debug_on(); + AuDbg("b%d, %s:%d\n", bindex, func, line); + AuDbgDentry(dentry); + AuDbgInode(inode); + au_debug_off(); + if (au_test_fuse(h_inode->i_sb)) + WARN_ON_ONCE(1); + else + BUG(); + } + } +} + +void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen) +{ + int err, i, j; + struct au_dcsub_pages dpages; + struct au_dpage *dpage; + struct dentry **dentries; + + err = au_dpages_init(&dpages, GFP_NOFS); + AuDebugOn(err); + err = au_dcsub_pages_rev_aufs(&dpages, parent, /*do_include*/1); + AuDebugOn(err); + for (i = dpages.ndpage - 1; !err && i >= 0; i--) { + dpage = dpages.dpages + i; + dentries = dpage->dentries; + for (j = dpage->ndentry - 1; !err && j >= 0; j--) + AuDebugOn(au_digen_test(dentries[j], sigen)); + } + au_dpages_free(&dpages); +} + +void au_dbg_verify_kthread(void) +{ + if (au_wkq_test()) { + au_dbg_blocked(); + /* + * It may be recursive, but udba=notify between two aufs mounts, + * where a single ro branch is shared, is not a problem. + */ + /* WARN_ON(1); */ + } +} + +/* ---------------------------------------------------------------------- */ + +int __init au_debug_init(void) +{ + aufs_bindex_t bindex; + struct au_vdir_destr destr; + + bindex = -1; + AuDebugOn(bindex >= 0); + + destr.len = -1; + AuDebugOn(destr.len < NAME_MAX); + +#ifdef CONFIG_4KSTACKS + pr_warn("CONFIG_4KSTACKS is defined.\n"); +#endif + + return 0; +} diff --git a/fs/aufs/debug.h b/fs/aufs/debug.h new file mode 100644 index 0000000000000..4ff77fb633ecb --- /dev/null +++ b/fs/aufs/debug.h @@ -0,0 +1,226 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * debug print functions + */ + +#ifndef __AUFS_DEBUG_H__ +#define __AUFS_DEBUG_H__ + +#ifdef __KERNEL__ + +#include +#include +#include +#include + +#ifdef CONFIG_AUFS_DEBUG +#define AuDebugOn(a) BUG_ON(a) + +/* module parameter */ +extern atomic_t aufs_debug; +static inline void au_debug_on(void) +{ + atomic_inc(&aufs_debug); +} +static inline void au_debug_off(void) +{ + atomic_dec_if_positive(&aufs_debug); +} + +static inline int au_debug_test(void) +{ + return atomic_read(&aufs_debug) > 0; +} +#else +#define AuDebugOn(a) do {} while (0) +AuStubVoid(au_debug_on, void) +AuStubVoid(au_debug_off, void) +AuStubInt0(au_debug_test, void) +#endif /* CONFIG_AUFS_DEBUG */ + +#define param_check_atomic_t(name, p) __param_check(name, p, atomic_t) + +/* ---------------------------------------------------------------------- */ + +/* debug print */ + +#define AuDbg(fmt, ...) do { \ + if (au_debug_test()) \ + pr_debug("DEBUG: " fmt, ##__VA_ARGS__); \ +} while (0) +#define AuLabel(l) AuDbg(#l "\n") +#define AuIOErr(fmt, ...) pr_err("I/O Error, " fmt, ##__VA_ARGS__) +#define AuWarn1(fmt, ...) do { \ + static unsigned char _c; \ + if (!_c++) \ + pr_warn(fmt, ##__VA_ARGS__); \ +} while (0) + +#define AuErr1(fmt, ...) do { \ + static unsigned char _c; \ + if (!_c++) \ + pr_err(fmt, ##__VA_ARGS__); \ +} while (0) + +#define AuIOErr1(fmt, ...) do { \ + static unsigned char _c; \ + if (!_c++) \ + AuIOErr(fmt, ##__VA_ARGS__); \ +} while (0) + +#define AuUnsupportMsg "This operation is not supported." \ + " Please report this application to aufs-users ML." +#define AuUnsupport(fmt, ...) do { \ + pr_err(AuUnsupportMsg "\n" fmt, ##__VA_ARGS__); \ + dump_stack(); \ +} while (0) + +#define AuTraceErr(e) do { \ + if (unlikely((e) < 0)) \ + AuDbg("err %d\n", (int)(e)); \ +} while (0) + +#define AuTraceErrPtr(p) do { \ + if (IS_ERR(p)) \ + AuDbg("err %ld\n", PTR_ERR(p)); \ +} while (0) + +/* dirty macros for debug print, use with "%.*s" and caution */ +#define AuLNPair(qstr) (qstr)->len, (qstr)->name + +/* ---------------------------------------------------------------------- */ + +struct dentry; +#ifdef CONFIG_AUFS_DEBUG +extern struct mutex au_dbg_mtx; +extern char *au_plevel; +struct au_nhash; +void au_dpri_whlist(struct au_nhash *whlist); +struct au_vdir; +void au_dpri_vdir(struct au_vdir *vdir); +struct inode; +void au_dpri_inode(struct inode *inode); +void au_dpri_dalias(struct inode *inode); +void au_dpri_dentry(struct dentry *dentry); +struct file; +void au_dpri_file(struct file *filp); +struct super_block; +void au_dpri_sb(struct super_block *sb); + +#define au_dbg_verify_dinode(d) __au_dbg_verify_dinode(d, __func__, __LINE__) +void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line); +void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen); +void au_dbg_verify_kthread(void); + +int __init au_debug_init(void); + +#define AuDbgWhlist(w) do { \ + mutex_lock(&au_dbg_mtx); \ + AuDbg(#w "\n"); \ + au_dpri_whlist(w); \ + mutex_unlock(&au_dbg_mtx); \ +} while (0) + +#define AuDbgVdir(v) do { \ + mutex_lock(&au_dbg_mtx); \ + AuDbg(#v "\n"); \ + au_dpri_vdir(v); \ + mutex_unlock(&au_dbg_mtx); \ +} while (0) + +#define AuDbgInode(i) do { \ + mutex_lock(&au_dbg_mtx); \ + AuDbg(#i "\n"); \ + au_dpri_inode(i); \ + mutex_unlock(&au_dbg_mtx); \ +} while (0) + +#define AuDbgDAlias(i) do { \ + mutex_lock(&au_dbg_mtx); \ + AuDbg(#i "\n"); \ + au_dpri_dalias(i); \ + mutex_unlock(&au_dbg_mtx); \ +} while (0) + +#define AuDbgDentry(d) do { \ + mutex_lock(&au_dbg_mtx); \ + AuDbg(#d "\n"); \ + au_dpri_dentry(d); \ + mutex_unlock(&au_dbg_mtx); \ +} while (0) + +#define AuDbgFile(f) do { \ + mutex_lock(&au_dbg_mtx); \ + AuDbg(#f "\n"); \ + au_dpri_file(f); \ + mutex_unlock(&au_dbg_mtx); \ +} while (0) + +#define AuDbgSb(sb) do { \ + mutex_lock(&au_dbg_mtx); \ + AuDbg(#sb "\n"); \ + au_dpri_sb(sb); \ + mutex_unlock(&au_dbg_mtx); \ +} while (0) + +#define AuDbgSym(addr) do { \ + char sym[KSYM_SYMBOL_LEN]; \ + sprint_symbol(sym, (unsigned long)addr); \ + AuDbg("%s\n", sym); \ +} while (0) +#else +AuStubVoid(au_dbg_verify_dinode, struct dentry *dentry) +AuStubVoid(au_dbg_verify_gen, struct dentry *parent, unsigned int sigen) +AuStubVoid(au_dbg_verify_kthread, void) +AuStubInt0(__init au_debug_init, void) + +#define AuDbgWhlist(w) do {} while (0) +#define AuDbgVdir(v) do {} while (0) +#define AuDbgInode(i) do {} while (0) +#define AuDbgDAlias(i) do {} while (0) +#define AuDbgDentry(d) do {} while (0) +#define AuDbgFile(f) do {} while (0) +#define AuDbgSb(sb) do {} while (0) +#define AuDbgSym(addr) do {} while (0) +#endif /* CONFIG_AUFS_DEBUG */ + +/* ---------------------------------------------------------------------- */ + +#ifdef CONFIG_AUFS_MAGIC_SYSRQ +int __init au_sysrq_init(void); +void au_sysrq_fin(void); + +#ifdef CONFIG_HW_CONSOLE +#define au_dbg_blocked() do { \ + WARN_ON(1); \ + handle_sysrq('w'); \ +} while (0) +#else +AuStubVoid(au_dbg_blocked, void) +#endif + +#else +AuStubInt0(__init au_sysrq_init, void) +AuStubVoid(au_sysrq_fin, void) +AuStubVoid(au_dbg_blocked, void) +#endif /* CONFIG_AUFS_MAGIC_SYSRQ */ + +#endif /* __KERNEL__ */ +#endif /* __AUFS_DEBUG_H__ */ diff --git a/fs/aufs/dentry.c b/fs/aufs/dentry.c new file mode 100644 index 0000000000000..48e7524a1be5d --- /dev/null +++ b/fs/aufs/dentry.c @@ -0,0 +1,1162 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * lookup and dentry operations + */ + +#include +#include "aufs.h" + +/* + * returns positive/negative dentry, NULL or an error. + * NULL means whiteout-ed or not-found. + */ +static struct dentry* +au_do_lookup(struct dentry *h_parent, struct dentry *dentry, + aufs_bindex_t bindex, struct au_do_lookup_args *args) +{ + struct dentry *h_dentry; + struct inode *h_inode; + struct au_branch *br; + struct path h_path; + int wh_found, opq; + unsigned char wh_able; + const unsigned char allow_neg = !!au_ftest_lkup(args->flags, ALLOW_NEG); + const unsigned char ignore_perm = !!au_ftest_lkup(args->flags, + IGNORE_PERM); + + wh_found = 0; + br = au_sbr(dentry->d_sb, bindex); + h_path.dentry = h_parent; + h_path.mnt = au_br_mnt(br); + wh_able = !!au_br_whable(br->br_perm); + if (wh_able) + wh_found = au_wh_test(&h_path, &args->whname, ignore_perm); + h_dentry = ERR_PTR(wh_found); + if (!wh_found) + goto real_lookup; + if (unlikely(wh_found < 0)) + goto out; + + /* We found a whiteout */ + /* au_set_dbbot(dentry, bindex); */ + au_set_dbwh(dentry, bindex); + if (!allow_neg) + return NULL; /* success */ + +real_lookup: + if (!ignore_perm) + h_dentry = vfsub_lkup_one(args->name, &h_path); + else + h_dentry = au_sio_lkup_one(args->name, &h_path); + if (IS_ERR(h_dentry)) { + if (PTR_ERR(h_dentry) == -ENAMETOOLONG + && !allow_neg) + h_dentry = NULL; + goto out; + } + + h_inode = d_inode(h_dentry); + if (d_is_negative(h_dentry)) { + if (!allow_neg) + goto out_neg; + } else if (wh_found + || (args->type && args->type != (h_inode->i_mode & S_IFMT))) + goto out_neg; + else if (au_ftest_lkup(args->flags, DIRREN) + /* && h_inode */ + && !au_dr_lkup_h_ino(args, bindex, h_inode->i_ino)) { + AuDbg("b%d %pd ignored hi%llu\n", bindex, h_dentry, + (unsigned long long)h_inode->i_ino); + goto out_neg; + } + + if (au_dbbot(dentry) <= bindex) + au_set_dbbot(dentry, bindex); + if (au_dbtop(dentry) < 0 || bindex < au_dbtop(dentry)) + au_set_dbtop(dentry, bindex); + au_set_h_dptr(dentry, bindex, h_dentry); + + if (!d_is_dir(h_dentry) + || !wh_able + || (d_really_is_positive(dentry) && !d_is_dir(dentry))) + goto out; /* success */ + + h_path.dentry = h_dentry; + inode_lock_shared_nested(h_inode, AuLsc_I_CHILD); + opq = au_diropq_test(&h_path); + inode_unlock_shared(h_inode); + if (opq > 0) + au_set_dbdiropq(dentry, bindex); + else if (unlikely(opq < 0)) { + au_set_h_dptr(dentry, bindex, NULL); + h_dentry = ERR_PTR(opq); + } + goto out; + +out_neg: + dput(h_dentry); + h_dentry = NULL; +out: + return h_dentry; +} + +static int au_test_shwh(struct super_block *sb, const struct qstr *name) +{ + if (unlikely(!au_opt_test(au_mntflags(sb), SHWH) + && !strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN))) + return -EPERM; + return 0; +} + +/* + * returns the number of lower positive dentries, + * otherwise an error. + * can be called at unlinking with @type is zero. + */ +int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t btop, + unsigned int flags) +{ + int npositive, err; + aufs_bindex_t bindex, btail, bdiropq; + unsigned char isdir, dirperm1, dirren; + struct au_do_lookup_args args = { + .flags = flags, + .name = &dentry->d_name + }; + struct dentry *parent; + struct super_block *sb; + + sb = dentry->d_sb; + err = au_test_shwh(sb, args.name); + if (unlikely(err)) + goto out; + + err = au_wh_name_alloc(&args.whname, args.name); + if (unlikely(err)) + goto out; + + isdir = !!d_is_dir(dentry); + dirperm1 = !!au_opt_test(au_mntflags(sb), DIRPERM1); + dirren = !!au_opt_test(au_mntflags(sb), DIRREN); + if (dirren) + au_fset_lkup(args.flags, DIRREN); + + npositive = 0; + parent = dget_parent(dentry); + btail = au_dbtaildir(parent); + for (bindex = btop; bindex <= btail; bindex++) { + struct dentry *h_parent, *h_dentry; + struct inode *h_inode, *h_dir; + struct au_branch *br; + + h_dentry = au_h_dptr(dentry, bindex); + if (h_dentry) { + if (d_is_positive(h_dentry)) + npositive++; + break; + } + h_parent = au_h_dptr(parent, bindex); + if (!h_parent || !d_is_dir(h_parent)) + continue; + + if (dirren) { + /* if the inum matches, then use the prepared name */ + err = au_dr_lkup_name(&args, bindex); + if (unlikely(err)) + goto out_parent; + } + + h_dir = d_inode(h_parent); + inode_lock_shared_nested(h_dir, AuLsc_I_PARENT); + h_dentry = au_do_lookup(h_parent, dentry, bindex, &args); + inode_unlock_shared(h_dir); + err = PTR_ERR(h_dentry); + if (IS_ERR(h_dentry)) + goto out_parent; + if (h_dentry) + au_fclr_lkup(args.flags, ALLOW_NEG); + if (dirperm1) + au_fset_lkup(args.flags, IGNORE_PERM); + + if (au_dbwh(dentry) == bindex) + break; + if (!h_dentry) + continue; + if (d_is_negative(h_dentry)) + continue; + h_inode = d_inode(h_dentry); + npositive++; + if (!args.type) + args.type = h_inode->i_mode & S_IFMT; + if (args.type != S_IFDIR) + break; + else if (isdir) { + /* the type of lower may be different */ + bdiropq = au_dbdiropq(dentry); + if (bdiropq >= 0 && bdiropq <= bindex) + break; + } + br = au_sbr(sb, bindex); + if (dirren + && au_dr_hino_test_add(&br->br_dirren, h_inode->i_ino, + /*add_ent*/NULL)) { + /* prepare next name to lookup */ + err = au_dr_lkup(&args, dentry, bindex); + if (unlikely(err)) + goto out_parent; + } + } + + if (npositive) { + AuLabel(positive); + au_update_dbtop(dentry); + } + err = npositive; + if (unlikely(!au_opt_test(au_mntflags(sb), UDBA_NONE) + && au_dbtop(dentry) < 0)) { + err = -EIO; + AuIOErr("both of real entry and whiteout found, %pd, err %d\n", + dentry, err); + } + +out_parent: + dput(parent); + au_kfree_try_rcu(args.whname.name); + if (dirren) + au_dr_lkup_fin(&args); +out: + return err; +} + +struct dentry *au_sio_lkup_one(struct qstr *name, struct path *ppath) +{ + struct dentry *dentry; + int wkq_err; + + if (!au_test_h_perm_sio(d_inode(ppath->dentry), MAY_EXEC)) + dentry = vfsub_lkup_one(name, ppath); + else { + struct vfsub_lkup_one_args args = { + .errp = &dentry, + .name = name, + .ppath = ppath + }; + + wkq_err = au_wkq_wait(vfsub_call_lkup_one, &args); + if (unlikely(wkq_err)) + dentry = ERR_PTR(wkq_err); + } + + return dentry; +} + +/* + * lookup @dentry on @bindex which should be negative. + */ +int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh) +{ + int err; + struct dentry *parent, *h_dentry; + struct au_branch *br; + struct path h_ppath; + + parent = dget_parent(dentry); + br = au_sbr(dentry->d_sb, bindex); + h_ppath.dentry = au_h_dptr(parent, bindex); + h_ppath.mnt = au_br_mnt(br); + if (wh) + h_dentry = au_whtmp_lkup(h_ppath.dentry, br, &dentry->d_name); + else + h_dentry = au_sio_lkup_one(&dentry->d_name, &h_ppath); + err = PTR_ERR(h_dentry); + if (IS_ERR(h_dentry)) + goto out; + if (unlikely(d_is_positive(h_dentry))) { + err = -EIO; + AuIOErr("%pd should be negative on b%d.\n", h_dentry, bindex); + dput(h_dentry); + goto out; + } + + err = 0; + if (bindex < au_dbtop(dentry)) + au_set_dbtop(dentry, bindex); + if (au_dbbot(dentry) < bindex) + au_set_dbbot(dentry, bindex); + au_set_h_dptr(dentry, bindex, h_dentry); + +out: + dput(parent); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* subset of struct inode */ +struct au_iattr { + unsigned long i_ino; + /* unsigned int i_nlink; */ + kuid_t i_uid; + kgid_t i_gid; + u64 i_version; +/* + loff_t i_size; + blkcnt_t i_blocks; +*/ + umode_t i_mode; +}; + +static void au_iattr_save(struct au_iattr *ia, struct inode *h_inode) +{ + ia->i_ino = h_inode->i_ino; + /* ia->i_nlink = h_inode->i_nlink; */ + ia->i_uid = h_inode->i_uid; + ia->i_gid = h_inode->i_gid; + ia->i_version = inode_query_iversion(h_inode); +/* + ia->i_size = h_inode->i_size; + ia->i_blocks = h_inode->i_blocks; +*/ + ia->i_mode = (h_inode->i_mode & S_IFMT); +} + +static int au_iattr_test(struct au_iattr *ia, struct inode *h_inode) +{ + return ia->i_ino != h_inode->i_ino + /* || ia->i_nlink != h_inode->i_nlink */ + || !uid_eq(ia->i_uid, h_inode->i_uid) + || !gid_eq(ia->i_gid, h_inode->i_gid) + || !inode_eq_iversion(h_inode, ia->i_version) +/* + || ia->i_size != h_inode->i_size + || ia->i_blocks != h_inode->i_blocks +*/ + || ia->i_mode != (h_inode->i_mode & S_IFMT); +} + +static int au_h_verify_dentry(struct dentry *h_dentry, struct dentry *h_parent, + struct au_branch *br) +{ + int err; + struct au_iattr ia; + struct inode *h_inode; + struct dentry *h_d; + struct super_block *h_sb; + struct path h_ppath; + + err = 0; + memset(&ia, -1, sizeof(ia)); + h_sb = h_dentry->d_sb; + h_inode = NULL; + if (d_is_positive(h_dentry)) { + h_inode = d_inode(h_dentry); + au_iattr_save(&ia, h_inode); + } else if (au_test_nfs(h_sb) || au_test_fuse(h_sb)) + /* nfs d_revalidate may return 0 for negative dentry */ + /* fuse d_revalidate always return 0 for negative dentry */ + goto out; + + /* main purpose is namei.c:cached_lookup() and d_revalidate */ + h_ppath.dentry = h_parent; + h_ppath.mnt = au_br_mnt(br); + h_d = vfsub_lkup_one(&h_dentry->d_name, &h_ppath); + err = PTR_ERR(h_d); + if (IS_ERR(h_d)) + goto out; + + err = 0; + if (unlikely(h_d != h_dentry + || d_inode(h_d) != h_inode + || (h_inode && au_iattr_test(&ia, h_inode)))) + err = au_busy_or_stale(); + dput(h_d); + +out: + AuTraceErr(err); + return err; +} + +int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir, + struct dentry *h_parent, struct au_branch *br) +{ + int err; + + err = 0; + if (udba == AuOpt_UDBA_REVAL + && !au_test_fs_remote(h_dentry->d_sb)) { + IMustLock(h_dir); + err = (d_inode(h_dentry->d_parent) != h_dir); + } else if (udba != AuOpt_UDBA_NONE) + err = au_h_verify_dentry(h_dentry, h_parent, br); + + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int au_do_refresh_hdentry(struct dentry *dentry, struct dentry *parent) +{ + int err; + aufs_bindex_t new_bindex, bindex, bbot, bwh, bdiropq; + struct au_hdentry tmp, *p, *q; + struct au_dinfo *dinfo; + struct super_block *sb; + + DiMustWriteLock(dentry); + + sb = dentry->d_sb; + dinfo = au_di(dentry); + bbot = dinfo->di_bbot; + bwh = dinfo->di_bwh; + bdiropq = dinfo->di_bdiropq; + bindex = dinfo->di_btop; + p = au_hdentry(dinfo, bindex); + for (; bindex <= bbot; bindex++, p++) { + if (!p->hd_dentry) + continue; + + new_bindex = au_br_index(sb, p->hd_id); + if (new_bindex == bindex) + continue; + + if (dinfo->di_bwh == bindex) + bwh = new_bindex; + if (dinfo->di_bdiropq == bindex) + bdiropq = new_bindex; + if (new_bindex < 0) { + au_hdput(p); + p->hd_dentry = NULL; + continue; + } + + /* swap two lower dentries, and loop again */ + q = au_hdentry(dinfo, new_bindex); + tmp = *q; + *q = *p; + *p = tmp; + if (tmp.hd_dentry) { + bindex--; + p--; + } + } + + dinfo->di_bwh = -1; + if (bwh >= 0 && bwh <= au_sbbot(sb) && au_sbr_whable(sb, bwh)) + dinfo->di_bwh = bwh; + + dinfo->di_bdiropq = -1; + if (bdiropq >= 0 + && bdiropq <= au_sbbot(sb) + && au_sbr_whable(sb, bdiropq)) + dinfo->di_bdiropq = bdiropq; + + err = -EIO; + dinfo->di_btop = -1; + dinfo->di_bbot = -1; + bbot = au_dbbot(parent); + bindex = 0; + p = au_hdentry(dinfo, bindex); + for (; bindex <= bbot; bindex++, p++) + if (p->hd_dentry) { + dinfo->di_btop = bindex; + break; + } + + if (dinfo->di_btop >= 0) { + bindex = bbot; + p = au_hdentry(dinfo, bindex); + for (; bindex >= 0; bindex--, p--) + if (p->hd_dentry) { + dinfo->di_bbot = bindex; + err = 0; + break; + } + } + + return err; +} + +static void au_do_hide(struct dentry *dentry) +{ + struct inode *inode; + + if (d_really_is_positive(dentry)) { + inode = d_inode(dentry); + if (!d_is_dir(dentry)) { + if (inode->i_nlink && !d_unhashed(dentry)) + drop_nlink(inode); + } else { + clear_nlink(inode); + /* stop next lookup */ + inode->i_flags |= S_DEAD; + } + smp_mb(); /* necessary? */ + } + d_drop(dentry); +} + +static int au_hide_children(struct dentry *parent) +{ + int err, i, j, ndentry; + struct au_dcsub_pages dpages; + struct au_dpage *dpage; + struct dentry *dentry; + + err = au_dpages_init(&dpages, GFP_NOFS); + if (unlikely(err)) + goto out; + err = au_dcsub_pages(&dpages, parent, NULL, NULL); + if (unlikely(err)) + goto out_dpages; + + /* in reverse order */ + for (i = dpages.ndpage - 1; i >= 0; i--) { + dpage = dpages.dpages + i; + ndentry = dpage->ndentry; + for (j = ndentry - 1; j >= 0; j--) { + dentry = dpage->dentries[j]; + if (dentry != parent) + au_do_hide(dentry); + } + } + +out_dpages: + au_dpages_free(&dpages); +out: + return err; +} + +static void au_hide(struct dentry *dentry) +{ + int err; + + AuDbgDentry(dentry); + if (d_is_dir(dentry)) { + /* shrink_dcache_parent(dentry); */ + err = au_hide_children(dentry); + if (unlikely(err)) + AuIOErr("%pd, failed hiding children, ignored %d\n", + dentry, err); + } + au_do_hide(dentry); +} + +/* + * By adding a dirty branch, a cached dentry may be affected in various ways. + * + * a dirty branch is added + * - on the top of layers + * - in the middle of layers + * - to the bottom of layers + * + * on the added branch there exists + * - a whiteout + * - a diropq + * - a same named entry + * + exist + * * negative --> positive + * * positive --> positive + * - type is unchanged + * - type is changed + * + doesn't exist + * * negative --> negative + * * positive --> negative (rejected by au_br_del() for non-dir case) + * - none + */ +static int au_refresh_by_dinfo(struct dentry *dentry, struct au_dinfo *dinfo, + struct au_dinfo *tmp) +{ + int err; + aufs_bindex_t bindex, bbot; + struct { + struct dentry *dentry; + struct inode *inode; + mode_t mode; + } orig_h, tmp_h = { + .dentry = NULL + }; + struct au_hdentry *hd; + struct inode *inode, *h_inode; + struct dentry *h_dentry; + + err = 0; + AuDebugOn(dinfo->di_btop < 0); + orig_h.mode = 0; + orig_h.dentry = au_hdentry(dinfo, dinfo->di_btop)->hd_dentry; + orig_h.inode = NULL; + if (d_is_positive(orig_h.dentry)) { + orig_h.inode = d_inode(orig_h.dentry); + orig_h.mode = orig_h.inode->i_mode & S_IFMT; + } + if (tmp->di_btop >= 0) { + tmp_h.dentry = au_hdentry(tmp, tmp->di_btop)->hd_dentry; + if (d_is_positive(tmp_h.dentry)) { + tmp_h.inode = d_inode(tmp_h.dentry); + tmp_h.mode = tmp_h.inode->i_mode & S_IFMT; + } + } + + inode = NULL; + if (d_really_is_positive(dentry)) + inode = d_inode(dentry); + if (!orig_h.inode) { + AuDbg("negative originally\n"); + if (inode) { + au_hide(dentry); + goto out; + } + AuDebugOn(inode); + AuDebugOn(dinfo->di_btop != dinfo->di_bbot); + AuDebugOn(dinfo->di_bdiropq != -1); + + if (!tmp_h.inode) { + AuDbg("negative --> negative\n"); + /* should have only one negative lower */ + if (tmp->di_btop >= 0 + && tmp->di_btop < dinfo->di_btop) { + AuDebugOn(tmp->di_btop != tmp->di_bbot); + AuDebugOn(dinfo->di_btop != dinfo->di_bbot); + au_set_h_dptr(dentry, dinfo->di_btop, NULL); + au_di_cp(dinfo, tmp); + hd = au_hdentry(tmp, tmp->di_btop); + au_set_h_dptr(dentry, tmp->di_btop, + dget(hd->hd_dentry)); + } + au_dbg_verify_dinode(dentry); + } else { + AuDbg("negative --> positive\n"); + /* + * similar to the behaviour of creating with bypassing + * aufs. + * unhash it in order to force an error in the + * succeeding create operation. + * we should not set S_DEAD here. + */ + d_drop(dentry); + /* au_di_swap(tmp, dinfo); */ + au_dbg_verify_dinode(dentry); + } + } else { + AuDbg("positive originally\n"); + /* inode may be NULL */ + AuDebugOn(inode && (inode->i_mode & S_IFMT) != orig_h.mode); + if (!tmp_h.inode) { + AuDbg("positive --> negative\n"); + /* or bypassing aufs */ + au_hide(dentry); + if (tmp->di_bwh >= 0 && tmp->di_bwh <= dinfo->di_btop) + dinfo->di_bwh = tmp->di_bwh; + if (inode) + err = au_refresh_hinode_self(inode); + au_dbg_verify_dinode(dentry); + } else if (orig_h.mode == tmp_h.mode) { + AuDbg("positive --> positive, same type\n"); + if (!S_ISDIR(orig_h.mode) + && dinfo->di_btop > tmp->di_btop) { + /* + * similar to the behaviour of removing and + * creating. + */ + au_hide(dentry); + if (inode) + err = au_refresh_hinode_self(inode); + au_dbg_verify_dinode(dentry); + } else { + /* fill empty slots */ + if (dinfo->di_btop > tmp->di_btop) + dinfo->di_btop = tmp->di_btop; + if (dinfo->di_bbot < tmp->di_bbot) + dinfo->di_bbot = tmp->di_bbot; + dinfo->di_bwh = tmp->di_bwh; + dinfo->di_bdiropq = tmp->di_bdiropq; + bbot = dinfo->di_bbot; + bindex = tmp->di_btop; + hd = au_hdentry(tmp, bindex); + for (; bindex <= bbot; bindex++, hd++) { + if (au_h_dptr(dentry, bindex)) + continue; + h_dentry = hd->hd_dentry; + if (!h_dentry) + continue; + AuDebugOn(d_is_negative(h_dentry)); + h_inode = d_inode(h_dentry); + AuDebugOn(orig_h.mode + != (h_inode->i_mode + & S_IFMT)); + au_set_h_dptr(dentry, bindex, + dget(h_dentry)); + } + if (inode) + err = au_refresh_hinode(inode, dentry); + au_dbg_verify_dinode(dentry); + } + } else { + AuDbg("positive --> positive, different type\n"); + /* similar to the behaviour of removing and creating */ + au_hide(dentry); + if (inode) + err = au_refresh_hinode_self(inode); + au_dbg_verify_dinode(dentry); + } + } + +out: + return err; +} + +void au_refresh_dop(struct dentry *dentry, int force_reval) +{ + const struct dentry_operations *dop + = force_reval ? &aufs_dop : dentry->d_sb->s_d_op; + static const unsigned int mask + = DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE; + + BUILD_BUG_ON(sizeof(mask) != sizeof(dentry->d_flags)); + + if (dentry->d_op == dop) + return; + + AuDbg("%pd\n", dentry); + spin_lock(&dentry->d_lock); + if (dop == &aufs_dop) + dentry->d_flags |= mask; + else + dentry->d_flags &= ~mask; + dentry->d_op = dop; + spin_unlock(&dentry->d_lock); +} + +int au_refresh_dentry(struct dentry *dentry, struct dentry *parent) +{ + int err, ebrange, nbr; + unsigned int sigen; + struct au_dinfo *dinfo, *tmp; + struct super_block *sb; + struct inode *inode; + + DiMustWriteLock(dentry); + AuDebugOn(IS_ROOT(dentry)); + AuDebugOn(d_really_is_negative(parent)); + + sb = dentry->d_sb; + sigen = au_sigen(sb); + err = au_digen_test(parent, sigen); + if (unlikely(err)) + goto out; + + nbr = au_sbbot(sb) + 1; + dinfo = au_di(dentry); + err = au_di_realloc(dinfo, nbr, /*may_shrink*/0); + if (unlikely(err)) + goto out; + ebrange = au_dbrange_test(dentry); + if (!ebrange) + ebrange = au_do_refresh_hdentry(dentry, parent); + + if (d_unhashed(dentry) || ebrange /* || dinfo->di_tmpfile */) { + AuDebugOn(au_dbtop(dentry) < 0 && au_dbbot(dentry) >= 0); + if (d_really_is_positive(dentry)) { + inode = d_inode(dentry); + err = au_refresh_hinode_self(inode); + } + au_dbg_verify_dinode(dentry); + if (!err) + goto out_dgen; /* success */ + goto out; + } + + /* temporary dinfo */ + AuDbgDentry(dentry); + err = -ENOMEM; + tmp = au_di_alloc(sb, AuLsc_DI_TMP); + if (unlikely(!tmp)) + goto out; + au_di_swap(tmp, dinfo); + /* returns the number of positive dentries */ + /* + * if current working dir is removed, it returns an error. + * but the dentry is legal. + */ + err = au_lkup_dentry(dentry, /*btop*/0, AuLkup_ALLOW_NEG); + AuDbgDentry(dentry); + au_di_swap(tmp, dinfo); + if (err == -ENOENT) + err = 0; + if (err >= 0) { + /* compare/refresh by dinfo */ + AuDbgDentry(dentry); + err = au_refresh_by_dinfo(dentry, dinfo, tmp); + au_dbg_verify_dinode(dentry); + AuTraceErr(err); + } + au_di_realloc(dinfo, nbr, /*may_shrink*/1); /* harmless if err */ + au_rw_write_unlock(&tmp->di_rwsem); + au_di_free(tmp); + if (unlikely(err)) + goto out; + +out_dgen: + au_update_digen(dentry); +out: + if (unlikely(err && !(dentry->d_flags & DCACHE_NFSFS_RENAMED))) { + AuIOErr("failed refreshing %pd, %d\n", dentry, err); + AuDbgDentry(dentry); + } + AuTraceErr(err); + return err; +} + +static int au_do_h_d_reval(struct dentry *h_dentry, unsigned int flags, + struct dentry *dentry, aufs_bindex_t bindex) +{ + int err, valid; + + err = 0; + if (!(h_dentry->d_flags & DCACHE_OP_REVALIDATE)) + goto out; + + AuDbg("b%d\n", bindex); + /* + * gave up supporting LOOKUP_CREATE/OPEN for lower fs, + * due to whiteout and branch permission. + */ + flags &= ~(/*LOOKUP_PARENT |*/ LOOKUP_OPEN | LOOKUP_CREATE + | LOOKUP_FOLLOW | LOOKUP_EXCL); + /* it may return tri-state */ + valid = h_dentry->d_op->d_revalidate(h_dentry, flags); + + if (unlikely(valid < 0)) + err = valid; + else if (!valid) + err = -EINVAL; + +out: + AuTraceErr(err); + return err; +} + +/* todo: remove this */ +static int h_d_revalidate(struct dentry *dentry, struct inode *inode, + unsigned int flags, int do_udba, int dirren) +{ + int err; + umode_t mode, h_mode; + aufs_bindex_t bindex, btail, btop, ibs, ibe; + unsigned char plus, unhashed, is_root, h_plus, h_nfs, tmpfile; + struct inode *h_inode, *h_cached_inode; + struct dentry *h_dentry; + struct qstr *name, *h_name; + + err = 0; + plus = 0; + mode = 0; + ibs = -1; + ibe = -1; + unhashed = !!d_unhashed(dentry); + is_root = !!IS_ROOT(dentry); + name = &dentry->d_name; + tmpfile = au_di(dentry)->di_tmpfile; + + /* + * Theoretically, REVAL test should be unnecessary in case of + * {FS,I}NOTIFY. + * But {fs,i}notify doesn't fire some necessary events, + * IN_ATTRIB for atime/nlink/pageio + * Let's do REVAL test too. + */ + if (do_udba && inode) { + mode = (inode->i_mode & S_IFMT); + plus = (inode->i_nlink > 0); + ibs = au_ibtop(inode); + ibe = au_ibbot(inode); + } + + btop = au_dbtop(dentry); + btail = btop; + if (inode && S_ISDIR(inode->i_mode)) + btail = au_dbtaildir(dentry); + for (bindex = btop; bindex <= btail; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; + + AuDbg("b%d, %pd\n", bindex, h_dentry); + h_nfs = !!au_test_nfs(h_dentry->d_sb); + spin_lock(&h_dentry->d_lock); + h_name = &h_dentry->d_name; + if (unlikely(do_udba + && !is_root + && ((!h_nfs + && (unhashed != !!d_unhashed(h_dentry) + || (!tmpfile && !dirren + && !au_qstreq(name, h_name)) + )) + || (h_nfs + && !(flags & LOOKUP_OPEN) + && (h_dentry->d_flags + & DCACHE_NFSFS_RENAMED))) + )) { + int h_unhashed; + + h_unhashed = d_unhashed(h_dentry); + spin_unlock(&h_dentry->d_lock); + AuDbg("unhash 0x%x 0x%x, %pd %pd\n", + unhashed, h_unhashed, dentry, h_dentry); + goto err; + } + spin_unlock(&h_dentry->d_lock); + + err = au_do_h_d_reval(h_dentry, flags, dentry, bindex); + if (unlikely(err)) + /* do not goto err, to keep the errno */ + break; + + /* todo: plink too? */ + if (!do_udba) + continue; + + /* UDBA tests */ + if (unlikely(!!inode != d_is_positive(h_dentry))) + goto err; + + h_inode = NULL; + if (d_is_positive(h_dentry)) + h_inode = d_inode(h_dentry); + h_plus = plus; + h_mode = mode; + h_cached_inode = h_inode; + if (h_inode) { + h_mode = (h_inode->i_mode & S_IFMT); + h_plus = (h_inode->i_nlink > 0); + } + if (inode && ibs <= bindex && bindex <= ibe) + h_cached_inode = au_h_iptr(inode, bindex); + + if (!h_nfs) { + if (unlikely(plus != h_plus && !tmpfile)) + goto err; + } else { + if (unlikely(!(h_dentry->d_flags & DCACHE_NFSFS_RENAMED) + && !is_root + && !IS_ROOT(h_dentry) + && unhashed != d_unhashed(h_dentry))) + goto err; + } + if (unlikely(mode != h_mode + || h_cached_inode != h_inode)) + goto err; + continue; + +err: + err = -EINVAL; + break; + } + + AuTraceErr(err); + return err; +} + +/* todo: consolidate with do_refresh() and au_reval_for_attr() */ +static int simple_reval_dpath(struct dentry *dentry, unsigned int sigen) +{ + int err; + struct dentry *parent; + + if (!au_digen_test(dentry, sigen)) + return 0; + + parent = dget_parent(dentry); + di_read_lock_parent(parent, AuLock_IR); + AuDebugOn(au_digen_test(parent, sigen)); + au_dbg_verify_gen(parent, sigen); + err = au_refresh_dentry(dentry, parent); + di_read_unlock(parent, AuLock_IR); + dput(parent); + AuTraceErr(err); + return err; +} + +int au_reval_dpath(struct dentry *dentry, unsigned int sigen) +{ + int err; + struct dentry *d, *parent; + + if (!au_ftest_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIR)) + return simple_reval_dpath(dentry, sigen); + + /* slow loop, keep it simple and stupid */ + /* cf: au_cpup_dirs() */ + err = 0; + parent = NULL; + while (au_digen_test(dentry, sigen)) { + d = dentry; + while (1) { + dput(parent); + parent = dget_parent(d); + if (!au_digen_test(parent, sigen)) + break; + d = parent; + } + + if (d != dentry) + di_write_lock_child2(d); + + /* someone might update our dentry while we were sleeping */ + if (au_digen_test(d, sigen)) { + /* + * todo: consolidate with simple_reval_dpath(), + * do_refresh() and au_reval_for_attr(). + */ + di_read_lock_parent(parent, AuLock_IR); + err = au_refresh_dentry(d, parent); + di_read_unlock(parent, AuLock_IR); + } + + if (d != dentry) + di_write_unlock(d); + dput(parent); + if (unlikely(err)) + break; + } + + return err; +} + +/* + * if valid returns 1, otherwise 0. + */ +static int aufs_d_revalidate(struct dentry *dentry, unsigned int flags) +{ + int valid, err; + unsigned int sigen; + unsigned char do_udba, dirren; + struct super_block *sb; + struct inode *inode; + + /* todo: support rcu-walk? */ + if (flags & LOOKUP_RCU) + return -ECHILD; + + valid = 0; + if (unlikely(!au_di(dentry))) + goto out; + + valid = 1; + sb = dentry->d_sb; + /* + * todo: very ugly + * i_mutex of parent dir may be held, + * but we should not return 'invalid' due to busy. + */ + err = aufs_read_lock(dentry, AuLock_FLUSH | AuLock_DW | AuLock_NOPLM); + if (unlikely(err)) { + valid = err; + AuTraceErr(err); + goto out; + } + inode = NULL; + if (d_really_is_positive(dentry)) + inode = d_inode(dentry); + if (unlikely(inode && au_is_bad_inode(inode))) { + err = -EINVAL; + AuTraceErr(err); + goto out_dgrade; + } + if (unlikely(au_dbrange_test(dentry))) { + err = -EINVAL; + AuTraceErr(err); + goto out_dgrade; + } + + sigen = au_sigen(sb); + if (au_digen_test(dentry, sigen)) { + AuDebugOn(IS_ROOT(dentry)); + err = au_reval_dpath(dentry, sigen); + if (unlikely(err)) { + AuTraceErr(err); + goto out_dgrade; + } + } + di_downgrade_lock(dentry, AuLock_IR); + + err = -EINVAL; + if (!(flags & (LOOKUP_OPEN | LOOKUP_EMPTY)) + && inode + && !(inode->i_state && I_LINKABLE) + && (IS_DEADDIR(inode) || !inode->i_nlink)) { + AuTraceErr(err); + goto out_inval; + } + + do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE); + if (do_udba && inode) { + aufs_bindex_t btop = au_ibtop(inode); + struct inode *h_inode; + + if (btop >= 0) { + h_inode = au_h_iptr(inode, btop); + if (h_inode && au_test_higen(inode, h_inode)) { + AuTraceErr(err); + goto out_inval; + } + } + } + + dirren = !!au_opt_test(au_mntflags(sb), DIRREN); + err = h_d_revalidate(dentry, inode, flags, do_udba, dirren); + if (unlikely(!err && do_udba && au_dbtop(dentry) < 0)) { + err = -EIO; + AuDbg("both of real entry and whiteout found, %p, err %d\n", + dentry, err); + } + goto out_inval; + +out_dgrade: + di_downgrade_lock(dentry, AuLock_IR); +out_inval: + aufs_read_unlock(dentry, AuLock_IR); + AuTraceErr(err); + valid = !err; +out: + if (!valid) { + AuDbg("%pd invalid, %d\n", dentry, valid); + d_drop(dentry); + } + return valid; +} + +static void aufs_d_release(struct dentry *dentry) +{ + if (au_di(dentry)) { + au_di_fin(dentry); + au_hn_di_reinit(dentry); + } +} + +const struct dentry_operations aufs_dop = { + .d_revalidate = aufs_d_revalidate, + .d_weak_revalidate = aufs_d_revalidate, + .d_release = aufs_d_release +}; + +/* aufs_dop without d_revalidate */ +const struct dentry_operations aufs_dop_noreval = { + .d_release = aufs_d_release +}; diff --git a/fs/aufs/dentry.h b/fs/aufs/dentry.h new file mode 100644 index 0000000000000..aa98f8f5c51d8 --- /dev/null +++ b/fs/aufs/dentry.h @@ -0,0 +1,268 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * lookup and dentry operations + */ + +#ifndef __AUFS_DENTRY_H__ +#define __AUFS_DENTRY_H__ + +#ifdef __KERNEL__ + +#include +#include "dirren.h" +#include "rwsem.h" + +struct au_hdentry { + struct dentry *hd_dentry; + aufs_bindex_t hd_id; +}; + +struct au_dinfo { + atomic_t di_generation; + + struct au_rwsem di_rwsem; + aufs_bindex_t di_btop, di_bbot, di_bwh, di_bdiropq; + unsigned char di_tmpfile; /* to allow the different name */ + struct au_hdentry *di_hdentry; + struct rcu_head rcu; +} ____cacheline_aligned_in_smp; + +/* ---------------------------------------------------------------------- */ + +/* flags for au_lkup_dentry() */ +#define AuLkup_ALLOW_NEG 1 +#define AuLkup_IGNORE_PERM (1 << 1) +#define AuLkup_DIRREN (1 << 2) +#define au_ftest_lkup(flags, name) ((flags) & AuLkup_##name) +#define au_fset_lkup(flags, name) \ + do { (flags) |= AuLkup_##name; } while (0) +#define au_fclr_lkup(flags, name) \ + do { (flags) &= ~AuLkup_##name; } while (0) + +#ifndef CONFIG_AUFS_DIRREN +#undef AuLkup_DIRREN +#define AuLkup_DIRREN 0 +#endif + +struct au_do_lookup_args { + unsigned int flags; + mode_t type; + struct qstr whname, *name; + struct au_dr_lookup dirren; +}; + +/* ---------------------------------------------------------------------- */ + +/* dentry.c */ +extern const struct dentry_operations aufs_dop, aufs_dop_noreval; +struct au_branch; +struct dentry *au_sio_lkup_one(struct qstr *name, struct path *ppath); +int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir, + struct dentry *h_parent, struct au_branch *br); + +int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t btop, + unsigned int flags); +int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh); +int au_refresh_dentry(struct dentry *dentry, struct dentry *parent); +int au_reval_dpath(struct dentry *dentry, unsigned int sigen); +void au_refresh_dop(struct dentry *dentry, int force_reval); + +/* dinfo.c */ +void au_di_init_once(void *_di); +struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc); +void au_di_free(struct au_dinfo *dinfo); +void au_di_swap(struct au_dinfo *a, struct au_dinfo *b); +void au_di_cp(struct au_dinfo *dst, struct au_dinfo *src); +int au_di_init(struct dentry *dentry); +void au_di_fin(struct dentry *dentry); +int au_di_realloc(struct au_dinfo *dinfo, int nbr, int may_shrink); + +void di_read_lock(struct dentry *d, int flags, unsigned int lsc); +void di_read_unlock(struct dentry *d, int flags); +void di_downgrade_lock(struct dentry *d, int flags); +void di_write_lock(struct dentry *d, unsigned int lsc); +void di_write_unlock(struct dentry *d); +void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir); +void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir); +void di_write_unlock2(struct dentry *d1, struct dentry *d2); + +struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex); +struct dentry *au_h_d_alias(struct dentry *dentry, aufs_bindex_t bindex); +aufs_bindex_t au_dbtail(struct dentry *dentry); +aufs_bindex_t au_dbtaildir(struct dentry *dentry); + +void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, + struct dentry *h_dentry); +int au_digen_test(struct dentry *dentry, unsigned int sigen); +int au_dbrange_test(struct dentry *dentry); +void au_update_digen(struct dentry *dentry); +void au_update_dbrange(struct dentry *dentry, int do_put_zero); +void au_update_dbtop(struct dentry *dentry); +void au_update_dbbot(struct dentry *dentry); +int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry); + +/* ---------------------------------------------------------------------- */ + +static inline struct au_dinfo *au_di(struct dentry *dentry) +{ + return dentry->d_fsdata; +} + +/* ---------------------------------------------------------------------- */ + +/* lock subclass for dinfo */ +enum { + AuLsc_DI_CHILD, /* child first */ + AuLsc_DI_CHILD2, /* rename(2), link(2), and cpup at hnotify */ + AuLsc_DI_CHILD3, /* copyup dirs */ + AuLsc_DI_PARENT, + AuLsc_DI_PARENT2, + AuLsc_DI_PARENT3, + AuLsc_DI_TMP /* temp for replacing dinfo */ +}; + +/* + * di_read_lock_child, di_write_lock_child, + * di_read_lock_child2, di_write_lock_child2, + * di_read_lock_child3, di_write_lock_child3, + * di_read_lock_parent, di_write_lock_parent, + * di_read_lock_parent2, di_write_lock_parent2, + * di_read_lock_parent3, di_write_lock_parent3, + */ +#define AuReadLockFunc(name, lsc) \ +static inline void di_read_lock_##name(struct dentry *d, int flags) \ +{ di_read_lock(d, flags, AuLsc_DI_##lsc); } + +#define AuWriteLockFunc(name, lsc) \ +static inline void di_write_lock_##name(struct dentry *d) \ +{ di_write_lock(d, AuLsc_DI_##lsc); } + +#define AuRWLockFuncs(name, lsc) \ + AuReadLockFunc(name, lsc) \ + AuWriteLockFunc(name, lsc) + +AuRWLockFuncs(child, CHILD); +AuRWLockFuncs(child2, CHILD2); +AuRWLockFuncs(child3, CHILD3); +AuRWLockFuncs(parent, PARENT); +AuRWLockFuncs(parent2, PARENT2); +AuRWLockFuncs(parent3, PARENT3); + +#undef AuReadLockFunc +#undef AuWriteLockFunc +#undef AuRWLockFuncs + +#define DiMustNoWaiters(d) AuRwMustNoWaiters(&au_di(d)->di_rwsem) +#define DiMustAnyLock(d) AuRwMustAnyLock(&au_di(d)->di_rwsem) +#define DiMustWriteLock(d) AuRwMustWriteLock(&au_di(d)->di_rwsem) + +/* ---------------------------------------------------------------------- */ + +/* todo: memory barrier? */ +static inline unsigned int au_digen(struct dentry *d) +{ + return atomic_read(&au_di(d)->di_generation); +} + +static inline void au_h_dentry_init(struct au_hdentry *hdentry) +{ + hdentry->hd_dentry = NULL; +} + +static inline struct au_hdentry *au_hdentry(struct au_dinfo *di, + aufs_bindex_t bindex) +{ + return di->di_hdentry + bindex; +} + +static inline void au_hdput(struct au_hdentry *hd) +{ + if (hd) + dput(hd->hd_dentry); +} + +static inline aufs_bindex_t au_dbtop(struct dentry *dentry) +{ + DiMustAnyLock(dentry); + return au_di(dentry)->di_btop; +} + +static inline aufs_bindex_t au_dbbot(struct dentry *dentry) +{ + DiMustAnyLock(dentry); + return au_di(dentry)->di_bbot; +} + +static inline aufs_bindex_t au_dbwh(struct dentry *dentry) +{ + DiMustAnyLock(dentry); + return au_di(dentry)->di_bwh; +} + +static inline aufs_bindex_t au_dbdiropq(struct dentry *dentry) +{ + DiMustAnyLock(dentry); + return au_di(dentry)->di_bdiropq; +} + +/* todo: hard/soft set? */ +static inline void au_set_dbtop(struct dentry *dentry, aufs_bindex_t bindex) +{ + DiMustWriteLock(dentry); + au_di(dentry)->di_btop = bindex; +} + +static inline void au_set_dbbot(struct dentry *dentry, aufs_bindex_t bindex) +{ + DiMustWriteLock(dentry); + au_di(dentry)->di_bbot = bindex; +} + +static inline void au_set_dbwh(struct dentry *dentry, aufs_bindex_t bindex) +{ + DiMustWriteLock(dentry); + /* dbwh can be outside of btop - bbot range */ + au_di(dentry)->di_bwh = bindex; +} + +static inline void au_set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex) +{ + DiMustWriteLock(dentry); + au_di(dentry)->di_bdiropq = bindex; +} + +/* ---------------------------------------------------------------------- */ + +#ifdef CONFIG_AUFS_HNOTIFY +static inline void au_digen_dec(struct dentry *d) +{ + atomic_dec(&au_di(d)->di_generation); +} + +static inline void au_hn_di_reinit(struct dentry *dentry) +{ + dentry->d_fsdata = NULL; +} +#else +AuStubVoid(au_hn_di_reinit, struct dentry *dentry __maybe_unused) +#endif /* CONFIG_AUFS_HNOTIFY */ + +#endif /* __KERNEL__ */ +#endif /* __AUFS_DENTRY_H__ */ diff --git a/fs/aufs/dinfo.c b/fs/aufs/dinfo.c new file mode 100644 index 0000000000000..a350f11814c25 --- /dev/null +++ b/fs/aufs/dinfo.c @@ -0,0 +1,554 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * dentry private data + */ + +#include "aufs.h" + +void au_di_init_once(void *_dinfo) +{ + struct au_dinfo *dinfo = _dinfo; + + au_rw_init(&dinfo->di_rwsem); +} + +struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc) +{ + struct au_dinfo *dinfo; + int nbr, i; + + dinfo = au_cache_alloc_dinfo(); + if (unlikely(!dinfo)) + goto out; + + nbr = au_sbbot(sb) + 1; + if (nbr <= 0) + nbr = 1; + dinfo->di_hdentry = kcalloc(nbr, sizeof(*dinfo->di_hdentry), GFP_NOFS); + if (dinfo->di_hdentry) { + au_rw_write_lock_nested(&dinfo->di_rwsem, lsc); + dinfo->di_btop = -1; + dinfo->di_bbot = -1; + dinfo->di_bwh = -1; + dinfo->di_bdiropq = -1; + dinfo->di_tmpfile = 0; + for (i = 0; i < nbr; i++) + dinfo->di_hdentry[i].hd_id = -1; + goto out; + } + + au_cache_free_dinfo(dinfo); + dinfo = NULL; + +out: + return dinfo; +} + +void au_di_free(struct au_dinfo *dinfo) +{ + struct au_hdentry *p; + aufs_bindex_t bbot, bindex; + + /* dentry may not be revalidated */ + bindex = dinfo->di_btop; + if (bindex >= 0) { + bbot = dinfo->di_bbot; + p = au_hdentry(dinfo, bindex); + while (bindex++ <= bbot) + au_hdput(p++); + } + au_kfree_try_rcu(dinfo->di_hdentry); + au_cache_free_dinfo(dinfo); +} + +void au_di_swap(struct au_dinfo *a, struct au_dinfo *b) +{ + struct au_hdentry *p; + aufs_bindex_t bi; + + AuRwMustWriteLock(&a->di_rwsem); + AuRwMustWriteLock(&b->di_rwsem); + +#define DiSwap(v, name) \ + do { \ + v = a->di_##name; \ + a->di_##name = b->di_##name; \ + b->di_##name = v; \ + } while (0) + + DiSwap(p, hdentry); + DiSwap(bi, btop); + DiSwap(bi, bbot); + DiSwap(bi, bwh); + DiSwap(bi, bdiropq); + /* smp_mb(); */ + +#undef DiSwap +} + +void au_di_cp(struct au_dinfo *dst, struct au_dinfo *src) +{ + AuRwMustWriteLock(&dst->di_rwsem); + AuRwMustWriteLock(&src->di_rwsem); + + dst->di_btop = src->di_btop; + dst->di_bbot = src->di_bbot; + dst->di_bwh = src->di_bwh; + dst->di_bdiropq = src->di_bdiropq; + /* smp_mb(); */ +} + +int au_di_init(struct dentry *dentry) +{ + int err; + struct super_block *sb; + struct au_dinfo *dinfo; + + err = 0; + sb = dentry->d_sb; + dinfo = au_di_alloc(sb, AuLsc_DI_CHILD); + if (dinfo) { + atomic_set(&dinfo->di_generation, au_sigen(sb)); + /* smp_mb(); */ /* atomic_set */ + dentry->d_fsdata = dinfo; + } else + err = -ENOMEM; + + return err; +} + +void au_di_fin(struct dentry *dentry) +{ + struct au_dinfo *dinfo; + + dinfo = au_di(dentry); + AuRwDestroy(&dinfo->di_rwsem); + au_di_free(dinfo); +} + +int au_di_realloc(struct au_dinfo *dinfo, int nbr, int may_shrink) +{ + int err, sz; + struct au_hdentry *hdp; + + AuRwMustWriteLock(&dinfo->di_rwsem); + + err = -ENOMEM; + sz = sizeof(*hdp) * (dinfo->di_bbot + 1); + if (!sz) + sz = sizeof(*hdp); + hdp = au_kzrealloc(dinfo->di_hdentry, sz, sizeof(*hdp) * nbr, GFP_NOFS, + may_shrink); + if (hdp) { + dinfo->di_hdentry = hdp; + err = 0; + } + + return err; +} + +/* ---------------------------------------------------------------------- */ + +static void do_ii_write_lock(struct inode *inode, unsigned int lsc) +{ + switch (lsc) { + case AuLsc_DI_CHILD: + ii_write_lock_child(inode); + break; + case AuLsc_DI_CHILD2: + ii_write_lock_child2(inode); + break; + case AuLsc_DI_CHILD3: + ii_write_lock_child3(inode); + break; + case AuLsc_DI_PARENT: + ii_write_lock_parent(inode); + break; + case AuLsc_DI_PARENT2: + ii_write_lock_parent2(inode); + break; + case AuLsc_DI_PARENT3: + ii_write_lock_parent3(inode); + break; + default: + BUG(); + } +} + +static void do_ii_read_lock(struct inode *inode, unsigned int lsc) +{ + switch (lsc) { + case AuLsc_DI_CHILD: + ii_read_lock_child(inode); + break; + case AuLsc_DI_CHILD2: + ii_read_lock_child2(inode); + break; + case AuLsc_DI_CHILD3: + ii_read_lock_child3(inode); + break; + case AuLsc_DI_PARENT: + ii_read_lock_parent(inode); + break; + case AuLsc_DI_PARENT2: + ii_read_lock_parent2(inode); + break; + case AuLsc_DI_PARENT3: + ii_read_lock_parent3(inode); + break; + default: + BUG(); + } +} + +void di_read_lock(struct dentry *d, int flags, unsigned int lsc) +{ + struct inode *inode; + + au_rw_read_lock_nested(&au_di(d)->di_rwsem, lsc); + if (d_really_is_positive(d)) { + inode = d_inode(d); + if (au_ftest_lock(flags, IW)) + do_ii_write_lock(inode, lsc); + else if (au_ftest_lock(flags, IR)) + do_ii_read_lock(inode, lsc); + } +} + +void di_read_unlock(struct dentry *d, int flags) +{ + struct inode *inode; + + if (d_really_is_positive(d)) { + inode = d_inode(d); + if (au_ftest_lock(flags, IW)) { + au_dbg_verify_dinode(d); + ii_write_unlock(inode); + } else if (au_ftest_lock(flags, IR)) { + au_dbg_verify_dinode(d); + ii_read_unlock(inode); + } + } + au_rw_read_unlock(&au_di(d)->di_rwsem); +} + +void di_downgrade_lock(struct dentry *d, int flags) +{ + if (d_really_is_positive(d) && au_ftest_lock(flags, IR)) + ii_downgrade_lock(d_inode(d)); + au_rw_dgrade_lock(&au_di(d)->di_rwsem); +} + +void di_write_lock(struct dentry *d, unsigned int lsc) +{ + au_rw_write_lock_nested(&au_di(d)->di_rwsem, lsc); + if (d_really_is_positive(d)) + do_ii_write_lock(d_inode(d), lsc); +} + +void di_write_unlock(struct dentry *d) +{ + au_dbg_verify_dinode(d); + if (d_really_is_positive(d)) + ii_write_unlock(d_inode(d)); + au_rw_write_unlock(&au_di(d)->di_rwsem); +} + +void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir) +{ + AuDebugOn(d1 == d2 + || d_inode(d1) == d_inode(d2) + || d1->d_sb != d2->d_sb); + + if ((isdir && au_test_subdir(d1, d2)) + || d1 < d2) { + di_write_lock_child(d1); + di_write_lock_child2(d2); + } else { + di_write_lock_child(d2); + di_write_lock_child2(d1); + } +} + +void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir) +{ + AuDebugOn(d1 == d2 + || d_inode(d1) == d_inode(d2) + || d1->d_sb != d2->d_sb); + + if ((isdir && au_test_subdir(d1, d2)) + || d1 < d2) { + di_write_lock_parent(d1); + di_write_lock_parent2(d2); + } else { + di_write_lock_parent(d2); + di_write_lock_parent2(d1); + } +} + +void di_write_unlock2(struct dentry *d1, struct dentry *d2) +{ + di_write_unlock(d1); + if (d_inode(d1) == d_inode(d2)) + au_rw_write_unlock(&au_di(d2)->di_rwsem); + else + di_write_unlock(d2); +} + +/* ---------------------------------------------------------------------- */ + +struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex) +{ + struct dentry *d; + + DiMustAnyLock(dentry); + + if (au_dbtop(dentry) < 0 || bindex < au_dbtop(dentry)) + return NULL; + AuDebugOn(bindex < 0); + d = au_hdentry(au_di(dentry), bindex)->hd_dentry; + AuDebugOn(d && au_dcount(d) <= 0); + return d; +} + +/* + * extended version of au_h_dptr(). + * returns a hashed and positive (or linkable) h_dentry in bindex, NULL, or + * error. + */ +struct dentry *au_h_d_alias(struct dentry *dentry, aufs_bindex_t bindex) +{ + struct dentry *h_dentry; + struct inode *inode, *h_inode; + + AuDebugOn(d_really_is_negative(dentry)); + + h_dentry = NULL; + if (au_dbtop(dentry) <= bindex + && bindex <= au_dbbot(dentry)) + h_dentry = au_h_dptr(dentry, bindex); + if (h_dentry && !au_d_linkable(h_dentry)) { + dget(h_dentry); + goto out; /* success */ + } + + inode = d_inode(dentry); + AuDebugOn(bindex < au_ibtop(inode)); + AuDebugOn(au_ibbot(inode) < bindex); + h_inode = au_h_iptr(inode, bindex); + h_dentry = d_find_alias(h_inode); + if (h_dentry) { + if (!IS_ERR(h_dentry)) { + if (!au_d_linkable(h_dentry)) + goto out; /* success */ + dput(h_dentry); + } else + goto out; + } + + if (au_opt_test(au_mntflags(dentry->d_sb), PLINK)) { + h_dentry = au_plink_lkup(inode, bindex); + AuDebugOn(!h_dentry); + if (!IS_ERR(h_dentry)) { + if (!au_d_hashed_positive(h_dentry)) + goto out; /* success */ + dput(h_dentry); + h_dentry = NULL; + } + } + +out: + AuDbgDentry(h_dentry); + return h_dentry; +} + +aufs_bindex_t au_dbtail(struct dentry *dentry) +{ + aufs_bindex_t bbot, bwh; + + bbot = au_dbbot(dentry); + if (0 <= bbot) { + bwh = au_dbwh(dentry); + if (!bwh) + return bwh; + if (0 < bwh && bwh < bbot) + return bwh - 1; + } + return bbot; +} + +aufs_bindex_t au_dbtaildir(struct dentry *dentry) +{ + aufs_bindex_t bbot, bopq; + + bbot = au_dbtail(dentry); + if (0 <= bbot) { + bopq = au_dbdiropq(dentry); + if (0 <= bopq && bopq < bbot) + bbot = bopq; + } + return bbot; +} + +/* ---------------------------------------------------------------------- */ + +void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, + struct dentry *h_dentry) +{ + struct au_dinfo *dinfo; + struct au_hdentry *hd; + struct au_branch *br; + + DiMustWriteLock(dentry); + + dinfo = au_di(dentry); + hd = au_hdentry(dinfo, bindex); + au_hdput(hd); + hd->hd_dentry = h_dentry; + if (h_dentry) { + br = au_sbr(dentry->d_sb, bindex); + hd->hd_id = br->br_id; + } +} + +int au_dbrange_test(struct dentry *dentry) +{ + int err; + aufs_bindex_t btop, bbot; + + err = 0; + btop = au_dbtop(dentry); + bbot = au_dbbot(dentry); + if (btop >= 0) + AuDebugOn(bbot < 0 && btop > bbot); + else { + err = -EIO; + AuDebugOn(bbot >= 0); + } + + return err; +} + +int au_digen_test(struct dentry *dentry, unsigned int sigen) +{ + int err; + + err = 0; + if (unlikely(au_digen(dentry) != sigen + || au_iigen_test(d_inode(dentry), sigen))) + err = -EIO; + + return err; +} + +void au_update_digen(struct dentry *dentry) +{ + atomic_set(&au_di(dentry)->di_generation, au_sigen(dentry->d_sb)); + /* smp_mb(); */ /* atomic_set */ +} + +void au_update_dbrange(struct dentry *dentry, int do_put_zero) +{ + struct au_dinfo *dinfo; + struct dentry *h_d; + struct au_hdentry *hdp; + aufs_bindex_t bindex, bbot; + + DiMustWriteLock(dentry); + + dinfo = au_di(dentry); + if (!dinfo || dinfo->di_btop < 0) + return; + + if (do_put_zero) { + bbot = dinfo->di_bbot; + bindex = dinfo->di_btop; + hdp = au_hdentry(dinfo, bindex); + for (; bindex <= bbot; bindex++, hdp++) { + h_d = hdp->hd_dentry; + if (h_d && d_is_negative(h_d)) + au_set_h_dptr(dentry, bindex, NULL); + } + } + + dinfo->di_btop = 0; + hdp = au_hdentry(dinfo, dinfo->di_btop); + for (; dinfo->di_btop <= dinfo->di_bbot; dinfo->di_btop++, hdp++) + if (hdp->hd_dentry) + break; + if (dinfo->di_btop > dinfo->di_bbot) { + dinfo->di_btop = -1; + dinfo->di_bbot = -1; + return; + } + + hdp = au_hdentry(dinfo, dinfo->di_bbot); + for (; dinfo->di_bbot >= 0; dinfo->di_bbot--, hdp--) + if (hdp->hd_dentry) + break; + AuDebugOn(dinfo->di_btop > dinfo->di_bbot || dinfo->di_bbot < 0); +} + +void au_update_dbtop(struct dentry *dentry) +{ + aufs_bindex_t bindex, bbot; + struct dentry *h_dentry; + + bbot = au_dbbot(dentry); + for (bindex = au_dbtop(dentry); bindex <= bbot; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; + if (d_is_positive(h_dentry)) { + au_set_dbtop(dentry, bindex); + return; + } + au_set_h_dptr(dentry, bindex, NULL); + } +} + +void au_update_dbbot(struct dentry *dentry) +{ + aufs_bindex_t bindex, btop; + struct dentry *h_dentry; + + btop = au_dbtop(dentry); + for (bindex = au_dbbot(dentry); bindex >= btop; bindex--) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; + if (d_is_positive(h_dentry)) { + au_set_dbbot(dentry, bindex); + return; + } + au_set_h_dptr(dentry, bindex, NULL); + } +} + +int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry) +{ + aufs_bindex_t bindex, bbot; + + bbot = au_dbbot(dentry); + for (bindex = au_dbtop(dentry); bindex <= bbot; bindex++) + if (au_h_dptr(dentry, bindex) == h_dentry) + return bindex; + return -1; +} diff --git a/fs/aufs/dir.c b/fs/aufs/dir.c new file mode 100644 index 0000000000000..053dd7f1fa95d --- /dev/null +++ b/fs/aufs/dir.c @@ -0,0 +1,763 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * directory operations + */ + +#include +#include +#include "aufs.h" + +void au_add_nlink(struct inode *dir, struct inode *h_dir) +{ + unsigned int nlink; + + AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode)); + + nlink = dir->i_nlink; + nlink += h_dir->i_nlink - 2; + if (h_dir->i_nlink < 2) + nlink += 2; + smp_mb(); /* for i_nlink */ + /* 0 can happen in revaliding */ + set_nlink(dir, nlink); +} + +void au_sub_nlink(struct inode *dir, struct inode *h_dir) +{ + unsigned int nlink; + + AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode)); + + nlink = dir->i_nlink; + nlink -= h_dir->i_nlink - 2; + if (h_dir->i_nlink < 2) + nlink -= 2; + smp_mb(); /* for i_nlink */ + /* nlink == 0 means the branch-fs is broken */ + set_nlink(dir, nlink); +} + +loff_t au_dir_size(struct file *file, struct dentry *dentry) +{ + loff_t sz; + aufs_bindex_t bindex, bbot; + struct file *h_file; + struct dentry *h_dentry; + + sz = 0; + if (file) { + AuDebugOn(!d_is_dir(file->f_path.dentry)); + + bbot = au_fbbot_dir(file); + for (bindex = au_fbtop(file); + bindex <= bbot && sz < KMALLOC_MAX_SIZE; + bindex++) { + h_file = au_hf_dir(file, bindex); + if (h_file && file_inode(h_file)) + sz += vfsub_f_size_read(h_file); + } + } else { + AuDebugOn(!dentry); + AuDebugOn(!d_is_dir(dentry)); + + bbot = au_dbtaildir(dentry); + for (bindex = au_dbtop(dentry); + bindex <= bbot && sz < KMALLOC_MAX_SIZE; + bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (h_dentry && d_is_positive(h_dentry)) + sz += i_size_read(d_inode(h_dentry)); + } + } + if (sz < KMALLOC_MAX_SIZE) + sz = roundup_pow_of_two(sz); + if (sz > KMALLOC_MAX_SIZE) + sz = KMALLOC_MAX_SIZE; + else if (sz < NAME_MAX) { + BUILD_BUG_ON(AUFS_RDBLK_DEF < NAME_MAX); + sz = AUFS_RDBLK_DEF; + } + return sz; +} + +struct au_dir_ts_arg { + struct dentry *dentry; + aufs_bindex_t brid; +}; + +static void au_do_dir_ts(void *arg) +{ + struct au_dir_ts_arg *a = arg; + struct au_dtime dt; + struct path h_path; + struct inode *dir, *h_dir; + struct super_block *sb; + struct au_branch *br; + struct au_hinode *hdir; + int err; + aufs_bindex_t btop, bindex; + + sb = a->dentry->d_sb; + if (d_really_is_negative(a->dentry)) + goto out; + /* no dir->i_mutex lock */ + aufs_read_lock(a->dentry, AuLock_DW); /* noflush */ + + dir = d_inode(a->dentry); + btop = au_ibtop(dir); + bindex = au_br_index(sb, a->brid); + if (bindex < btop) + goto out_unlock; + + br = au_sbr(sb, bindex); + h_path.dentry = au_h_dptr(a->dentry, bindex); + if (!h_path.dentry) + goto out_unlock; + h_path.mnt = au_br_mnt(br); + au_dtime_store(&dt, a->dentry, &h_path); + + br = au_sbr(sb, btop); + if (!au_br_writable(br->br_perm)) + goto out_unlock; + h_path.dentry = au_h_dptr(a->dentry, btop); + h_path.mnt = au_br_mnt(br); + err = vfsub_mnt_want_write(h_path.mnt); + if (err) + goto out_unlock; + hdir = au_hi(dir, btop); + au_hn_inode_lock_nested(hdir, AuLsc_I_PARENT); + h_dir = au_h_iptr(dir, btop); + if (h_dir->i_nlink + && timespec64_compare(&h_dir->i_mtime, &dt.dt_mtime) < 0) { + dt.dt_h_path = h_path; + au_dtime_revert(&dt); + } + au_hn_inode_unlock(hdir); + vfsub_mnt_drop_write(h_path.mnt); + au_cpup_attr_timesizes(dir); + +out_unlock: + aufs_read_unlock(a->dentry, AuLock_DW); +out: + dput(a->dentry); + au_nwt_done(&au_sbi(sb)->si_nowait); + au_kfree_try_rcu(arg); +} + +void au_dir_ts(struct inode *dir, aufs_bindex_t bindex) +{ + int perm, wkq_err; + aufs_bindex_t btop; + struct au_dir_ts_arg *arg; + struct dentry *dentry; + struct super_block *sb; + + IMustLock(dir); + + dentry = d_find_any_alias(dir); + AuDebugOn(!dentry); + sb = dentry->d_sb; + btop = au_ibtop(dir); + if (btop == bindex) { + au_cpup_attr_timesizes(dir); + goto out; + } + + perm = au_sbr_perm(sb, btop); + if (!au_br_writable(perm)) + goto out; + + arg = kmalloc(sizeof(*arg), GFP_NOFS); + if (!arg) + goto out; + + arg->dentry = dget(dentry); /* will be dput-ted by au_do_dir_ts() */ + arg->brid = au_sbr_id(sb, bindex); + wkq_err = au_wkq_nowait(au_do_dir_ts, arg, sb, /*flags*/0); + if (unlikely(wkq_err)) { + pr_err("wkq %d\n", wkq_err); + dput(dentry); + au_kfree_try_rcu(arg); + } + +out: + dput(dentry); +} + +/* ---------------------------------------------------------------------- */ + +static int reopen_dir(struct file *file) +{ + int err; + unsigned int flags; + aufs_bindex_t bindex, btail, btop; + struct dentry *dentry, *h_dentry; + struct file *h_file; + + /* open all lower dirs */ + dentry = file->f_path.dentry; + btop = au_dbtop(dentry); + for (bindex = au_fbtop(file); bindex < btop; bindex++) + au_set_h_fptr(file, bindex, NULL); + au_set_fbtop(file, btop); + + btail = au_dbtaildir(dentry); + for (bindex = au_fbbot_dir(file); btail < bindex; bindex--) + au_set_h_fptr(file, bindex, NULL); + au_set_fbbot_dir(file, btail); + + flags = vfsub_file_flags(file); + for (bindex = btop; bindex <= btail; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; + h_file = au_hf_dir(file, bindex); + if (h_file) + continue; + + h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; /* close all? */ + au_set_h_fptr(file, bindex, h_file); + } + au_update_figen(file); + /* todo: necessary? */ + /* file->f_ra = h_file->f_ra; */ + err = 0; + +out: + return err; +} + +static int do_open_dir(struct file *file, int flags, struct file *h_file) +{ + int err; + aufs_bindex_t bindex, btail; + struct dentry *dentry, *h_dentry; + struct vfsmount *mnt; + + FiMustWriteLock(file); + AuDebugOn(h_file); + + err = 0; + mnt = file->f_path.mnt; + dentry = file->f_path.dentry; + file->f_version = inode_query_iversion(d_inode(dentry)); + bindex = au_dbtop(dentry); + au_set_fbtop(file, bindex); + btail = au_dbtaildir(dentry); + au_set_fbbot_dir(file, btail); + for (; !err && bindex <= btail; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; + + err = vfsub_test_mntns(mnt, h_dentry->d_sb); + if (unlikely(err)) + break; + h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); + if (IS_ERR(h_file)) { + err = PTR_ERR(h_file); + break; + } + au_set_h_fptr(file, bindex, h_file); + } + au_update_figen(file); + /* todo: necessary? */ + /* file->f_ra = h_file->f_ra; */ + if (!err) + return 0; /* success */ + + /* close all */ + for (bindex = au_fbtop(file); bindex <= btail; bindex++) + au_set_h_fptr(file, bindex, NULL); + au_set_fbtop(file, -1); + au_set_fbbot_dir(file, -1); + + return err; +} + +static int aufs_open_dir(struct inode *inode __maybe_unused, + struct file *file) +{ + int err; + struct super_block *sb; + struct au_fidir *fidir; + + err = -ENOMEM; + sb = file->f_path.dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH); + fidir = au_fidir_alloc(sb); + if (fidir) { + struct au_do_open_args args = { + .open = do_open_dir, + .fidir = fidir + }; + err = au_do_open(file, &args); + if (unlikely(err)) + au_kfree_rcu(fidir); + } + si_read_unlock(sb); + return err; +} + +static int aufs_release_dir(struct inode *inode __maybe_unused, + struct file *file) +{ + struct au_vdir *vdir_cache; + struct au_finfo *finfo; + struct au_fidir *fidir; + struct au_hfile *hf; + aufs_bindex_t bindex, bbot; + + finfo = au_fi(file); + fidir = finfo->fi_hdir; + if (fidir) { + au_hbl_del(&finfo->fi_hlist, + &au_sbi(file->f_path.dentry->d_sb)->si_files); + vdir_cache = fidir->fd_vdir_cache; /* lock-free */ + if (vdir_cache) + au_vdir_free(vdir_cache); + + bindex = finfo->fi_btop; + if (bindex >= 0) { + hf = fidir->fd_hfile + bindex; + /* + * calls fput() instead of filp_close(), + * since no dnotify or lock for the lower file. + */ + bbot = fidir->fd_bbot; + for (; bindex <= bbot; bindex++, hf++) + if (hf->hf_file) + au_hfput(hf, /*execed*/0); + } + au_kfree_rcu(fidir); + finfo->fi_hdir = NULL; + } + au_finfo_fin(file); + return 0; +} + +/* ---------------------------------------------------------------------- */ + +static int au_do_flush_dir(struct file *file, fl_owner_t id) +{ + int err; + aufs_bindex_t bindex, bbot; + struct file *h_file; + + err = 0; + bbot = au_fbbot_dir(file); + for (bindex = au_fbtop(file); !err && bindex <= bbot; bindex++) { + h_file = au_hf_dir(file, bindex); + if (h_file) + err = vfsub_flush(h_file, id); + } + return err; +} + +static int aufs_flush_dir(struct file *file, fl_owner_t id) +{ + return au_do_flush(file, id, au_do_flush_dir); +} + +/* ---------------------------------------------------------------------- */ + +static int au_do_fsync_dir_no_file(struct dentry *dentry, int datasync) +{ + int err; + aufs_bindex_t bbot, bindex; + struct inode *inode; + struct super_block *sb; + + err = 0; + sb = dentry->d_sb; + inode = d_inode(dentry); + IMustLock(inode); + bbot = au_dbbot(dentry); + for (bindex = au_dbtop(dentry); !err && bindex <= bbot; bindex++) { + struct path h_path; + + if (au_test_ro(sb, bindex, inode)) + continue; + h_path.dentry = au_h_dptr(dentry, bindex); + if (!h_path.dentry) + continue; + + h_path.mnt = au_sbr_mnt(sb, bindex); + err = vfsub_fsync(NULL, &h_path, datasync); + } + + return err; +} + +static int au_do_fsync_dir(struct file *file, int datasync) +{ + int err; + aufs_bindex_t bbot, bindex; + struct file *h_file; + struct super_block *sb; + struct inode *inode; + + err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1, /*fi_lsc*/0); + if (unlikely(err)) + goto out; + + inode = file_inode(file); + sb = inode->i_sb; + bbot = au_fbbot_dir(file); + for (bindex = au_fbtop(file); !err && bindex <= bbot; bindex++) { + h_file = au_hf_dir(file, bindex); + if (!h_file || au_test_ro(sb, bindex, inode)) + continue; + + err = vfsub_fsync(h_file, &h_file->f_path, datasync); + } + +out: + return err; +} + +/* + * @file may be NULL + */ +static int aufs_fsync_dir(struct file *file, loff_t start, loff_t end, + int datasync) +{ + int err; + struct dentry *dentry; + struct inode *inode; + struct super_block *sb; + + err = 0; + dentry = file->f_path.dentry; + inode = d_inode(dentry); + inode_lock(inode); + sb = dentry->d_sb; + si_noflush_read_lock(sb); + if (file) + err = au_do_fsync_dir(file, datasync); + else { + di_write_lock_child(dentry); + err = au_do_fsync_dir_no_file(dentry, datasync); + } + au_cpup_attr_timesizes(inode); + di_write_unlock(dentry); + if (file) + fi_write_unlock(file); + + si_read_unlock(sb); + inode_unlock(inode); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int aufs_iterate_shared(struct file *file, struct dir_context *ctx) +{ + int err; + struct dentry *dentry; + struct inode *inode, *h_inode; + struct super_block *sb; + + AuDbg("%pD, ctx{%ps, %llu}\n", file, ctx->actor, ctx->pos); + + dentry = file->f_path.dentry; + inode = d_inode(dentry); + IMustLock(inode); + + sb = dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH); + err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1, /*fi_lsc*/0); + if (unlikely(err)) + goto out; + err = au_alive_dir(dentry); + if (!err) + err = au_vdir_init(file); + di_downgrade_lock(dentry, AuLock_IR); + if (unlikely(err)) + goto out_unlock; + + h_inode = au_h_iptr(inode, au_ibtop(inode)); + if (!au_test_nfsd()) { + err = au_vdir_fill_de(file, ctx); + fsstack_copy_attr_atime(inode, h_inode); + } else { + /* + * nfsd filldir may call lookup_one_len(), vfs_getattr(), + * encode_fh() and others. + */ + atomic_inc(&h_inode->i_count); + di_read_unlock(dentry, AuLock_IR); + si_read_unlock(sb); + err = au_vdir_fill_de(file, ctx); + fsstack_copy_attr_atime(inode, h_inode); + fi_write_unlock(file); + iput(h_inode); + + AuTraceErr(err); + return err; + } + +out_unlock: + di_read_unlock(dentry, AuLock_IR); + fi_write_unlock(file); +out: + si_read_unlock(sb); + return err; +} + +/* ---------------------------------------------------------------------- */ + +#define AuTestEmpty_WHONLY 1 +#define AuTestEmpty_CALLED (1 << 1) +#define AuTestEmpty_SHWH (1 << 2) +#define au_ftest_testempty(flags, name) ((flags) & AuTestEmpty_##name) +#define au_fset_testempty(flags, name) \ + do { (flags) |= AuTestEmpty_##name; } while (0) +#define au_fclr_testempty(flags, name) \ + do { (flags) &= ~AuTestEmpty_##name; } while (0) + +#ifndef CONFIG_AUFS_SHWH +#undef AuTestEmpty_SHWH +#define AuTestEmpty_SHWH 0 +#endif + +struct test_empty_arg { + struct dir_context ctx; + struct au_nhash *whlist; + unsigned int flags; + int err; + aufs_bindex_t bindex; +}; + +static int test_empty_cb(struct dir_context *ctx, const char *__name, + int namelen, loff_t offset __maybe_unused, u64 ino, + unsigned int d_type) +{ + struct test_empty_arg *arg = container_of(ctx, struct test_empty_arg, + ctx); + char *name = (void *)__name; + + arg->err = 0; + au_fset_testempty(arg->flags, CALLED); + /* smp_mb(); */ + if (name[0] == '.' + && (namelen == 1 || (name[1] == '.' && namelen == 2))) + goto out; /* success */ + + if (namelen <= AUFS_WH_PFX_LEN + || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { + if (au_ftest_testempty(arg->flags, WHONLY) + && !au_nhash_test_known_wh(arg->whlist, name, namelen)) + arg->err = -ENOTEMPTY; + goto out; + } + + name += AUFS_WH_PFX_LEN; + namelen -= AUFS_WH_PFX_LEN; + if (!au_nhash_test_known_wh(arg->whlist, name, namelen)) + arg->err = au_nhash_append_wh + (arg->whlist, name, namelen, ino, d_type, arg->bindex, + au_ftest_testempty(arg->flags, SHWH)); + +out: + /* smp_mb(); */ + AuTraceErr(arg->err); + return arg->err; +} + +static int do_test_empty(struct dentry *dentry, struct test_empty_arg *arg) +{ + int err; + struct file *h_file; + struct au_branch *br; + + h_file = au_h_open(dentry, arg->bindex, + O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_LARGEFILE, + /*file*/NULL, /*force_wr*/0); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; + + err = 0; + if (!au_opt_test(au_mntflags(dentry->d_sb), UDBA_NONE) + && !file_inode(h_file)->i_nlink) + goto out_put; + + do { + arg->err = 0; + au_fclr_testempty(arg->flags, CALLED); + /* smp_mb(); */ + err = vfsub_iterate_dir(h_file, &arg->ctx); + if (err >= 0) + err = arg->err; + } while (!err && au_ftest_testempty(arg->flags, CALLED)); + +out_put: + fput(h_file); + br = au_sbr(dentry->d_sb, arg->bindex); + au_lcnt_dec(&br->br_nfiles); +out: + return err; +} + +struct do_test_empty_args { + int *errp; + struct dentry *dentry; + struct test_empty_arg *arg; +}; + +static void call_do_test_empty(void *args) +{ + struct do_test_empty_args *a = args; + *a->errp = do_test_empty(a->dentry, a->arg); +} + +static int sio_test_empty(struct dentry *dentry, struct test_empty_arg *arg) +{ + int err, wkq_err; + struct dentry *h_dentry; + struct inode *h_inode; + + h_dentry = au_h_dptr(dentry, arg->bindex); + h_inode = d_inode(h_dentry); + /* todo: i_mode changes anytime? */ + inode_lock_shared_nested(h_inode, AuLsc_I_CHILD); + err = au_test_h_perm_sio(h_inode, MAY_EXEC | MAY_READ); + inode_unlock_shared(h_inode); + if (!err) + err = do_test_empty(dentry, arg); + else { + struct do_test_empty_args args = { + .errp = &err, + .dentry = dentry, + .arg = arg + }; + unsigned int flags = arg->flags; + + wkq_err = au_wkq_wait(call_do_test_empty, &args); + if (unlikely(wkq_err)) + err = wkq_err; + arg->flags = flags; + } + + return err; +} + +int au_test_empty_lower(struct dentry *dentry) +{ + int err; + unsigned int rdhash; + aufs_bindex_t bindex, btop, btail; + struct au_nhash whlist; + struct test_empty_arg arg = { + .ctx = { + .actor = test_empty_cb + } + }; + int (*test_empty)(struct dentry *dentry, struct test_empty_arg *arg); + + SiMustAnyLock(dentry->d_sb); + + rdhash = au_sbi(dentry->d_sb)->si_rdhash; + if (!rdhash) + rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, dentry)); + err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS); + if (unlikely(err)) + goto out; + + arg.flags = 0; + arg.whlist = &whlist; + btop = au_dbtop(dentry); + if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)) + au_fset_testempty(arg.flags, SHWH); + test_empty = do_test_empty; + if (au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1)) + test_empty = sio_test_empty; + arg.bindex = btop; + err = test_empty(dentry, &arg); + if (unlikely(err)) + goto out_whlist; + + au_fset_testempty(arg.flags, WHONLY); + btail = au_dbtaildir(dentry); + for (bindex = btop + 1; !err && bindex <= btail; bindex++) { + struct dentry *h_dentry; + + h_dentry = au_h_dptr(dentry, bindex); + if (h_dentry && d_is_positive(h_dentry)) { + arg.bindex = bindex; + err = test_empty(dentry, &arg); + } + } + +out_whlist: + au_nhash_wh_free(&whlist); +out: + return err; +} + +int au_test_empty(struct dentry *dentry, struct au_nhash *whlist) +{ + int err; + struct test_empty_arg arg = { + .ctx = { + .actor = test_empty_cb + } + }; + aufs_bindex_t bindex, btail; + + err = 0; + arg.whlist = whlist; + arg.flags = AuTestEmpty_WHONLY; + if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)) + au_fset_testempty(arg.flags, SHWH); + btail = au_dbtaildir(dentry); + for (bindex = au_dbtop(dentry); !err && bindex <= btail; bindex++) { + struct dentry *h_dentry; + + h_dentry = au_h_dptr(dentry, bindex); + if (h_dentry && d_is_positive(h_dentry)) { + arg.bindex = bindex; + err = sio_test_empty(dentry, &arg); + } + } + + return err; +} + +/* ---------------------------------------------------------------------- */ + +const struct file_operations aufs_dir_fop = { + .owner = THIS_MODULE, + .llseek = default_llseek, + .read = generic_read_dir, + .iterate_shared = aufs_iterate_shared, + .unlocked_ioctl = aufs_ioctl_dir, +#ifdef CONFIG_COMPAT + .compat_ioctl = aufs_compat_ioctl_dir, +#endif + .open = aufs_open_dir, + .release = aufs_release_dir, + .flush = aufs_flush_dir, + .fsync = aufs_fsync_dir +}; diff --git a/fs/aufs/dir.h b/fs/aufs/dir.h new file mode 100644 index 0000000000000..7a6e004307e9e --- /dev/null +++ b/fs/aufs/dir.h @@ -0,0 +1,134 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * directory operations + */ + +#ifndef __AUFS_DIR_H__ +#define __AUFS_DIR_H__ + +#ifdef __KERNEL__ + +#include + +/* ---------------------------------------------------------------------- */ + +/* need to be faster and smaller */ + +struct au_nhash { + unsigned int nh_num; + struct hlist_head *nh_head; +}; + +struct au_vdir_destr { + unsigned char len; + unsigned char name[]; +} __packed; + +struct au_vdir_dehstr { + struct hlist_node hash; + struct au_vdir_destr *str; + struct rcu_head rcu; +} ____cacheline_aligned_in_smp; + +struct au_vdir_de { + ino_t de_ino; + unsigned char de_type; + /* caution: packed */ + struct au_vdir_destr de_str; +} __packed; + +struct au_vdir_wh { + struct hlist_node wh_hash; +#ifdef CONFIG_AUFS_SHWH + ino_t wh_ino; + aufs_bindex_t wh_bindex; + unsigned char wh_type; +#else + aufs_bindex_t wh_bindex; +#endif + /* caution: packed */ + struct au_vdir_destr wh_str; +} __packed; + +union au_vdir_deblk_p { + unsigned char *deblk; + struct au_vdir_de *de; +}; + +struct au_vdir { + unsigned char **vd_deblk; + unsigned long vd_nblk; + struct { + unsigned long ul; + union au_vdir_deblk_p p; + } vd_last; + + u64 vd_version; + unsigned int vd_deblk_sz; + unsigned long vd_jiffy; + struct rcu_head rcu; +} ____cacheline_aligned_in_smp; + +/* ---------------------------------------------------------------------- */ + +/* dir.c */ +extern const struct file_operations aufs_dir_fop; +void au_add_nlink(struct inode *dir, struct inode *h_dir); +void au_sub_nlink(struct inode *dir, struct inode *h_dir); +loff_t au_dir_size(struct file *file, struct dentry *dentry); +void au_dir_ts(struct inode *dir, aufs_bindex_t bsrc); +int au_test_empty_lower(struct dentry *dentry); +int au_test_empty(struct dentry *dentry, struct au_nhash *whlist); + +/* vdir.c */ +unsigned int au_rdhash_est(loff_t sz); +int au_nhash_alloc(struct au_nhash *nhash, unsigned int num_hash, gfp_t gfp); +void au_nhash_wh_free(struct au_nhash *whlist); +int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt, + int limit); +int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int nlen); +int au_nhash_append_wh(struct au_nhash *whlist, char *name, int nlen, ino_t ino, + unsigned int d_type, aufs_bindex_t bindex, + unsigned char shwh); +void au_vdir_free(struct au_vdir *vdir); +int au_vdir_init(struct file *file); +int au_vdir_fill_de(struct file *file, struct dir_context *ctx); + +/* ioctl.c */ +long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg); + +#ifdef CONFIG_AUFS_RDU +/* rdu.c */ +long au_rdu_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +#ifdef CONFIG_COMPAT +long au_rdu_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); +#endif +#else +AuStub(long, au_rdu_ioctl, return -EINVAL, struct file *file, + unsigned int cmd, unsigned long arg) +#ifdef CONFIG_COMPAT +AuStub(long, au_rdu_compat_ioctl, return -EINVAL, struct file *file, + unsigned int cmd, unsigned long arg) +#endif +#endif + +#endif /* __KERNEL__ */ +#endif /* __AUFS_DIR_H__ */ diff --git a/fs/aufs/dirren.c b/fs/aufs/dirren.c new file mode 100644 index 0000000000000..341b706f5e13d --- /dev/null +++ b/fs/aufs/dirren.c @@ -0,0 +1,1315 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2017-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * special handling in renaming a directory + * in order to support looking-up the before-renamed name on the lower readonly + * branches + */ + +#include +#include "aufs.h" + +static void au_dr_hino_del(struct au_dr_br *dr, struct au_dr_hino *ent) +{ + int idx; + + idx = au_dr_ihash(ent->dr_h_ino); + au_hbl_del(&ent->dr_hnode, dr->dr_h_ino + idx); +} + +static int au_dr_hino_test_empty(struct au_dr_br *dr) +{ + int ret, i; + struct hlist_bl_head *hbl; + + ret = 1; + for (i = 0; ret && i < AuDirren_NHASH; i++) { + hbl = dr->dr_h_ino + i; + hlist_bl_lock(hbl); + ret &= hlist_bl_empty(hbl); + hlist_bl_unlock(hbl); + } + + return ret; +} + +static struct au_dr_hino *au_dr_hino_find(struct au_dr_br *dr, ino_t ino) +{ + struct au_dr_hino *found, *ent; + struct hlist_bl_head *hbl; + struct hlist_bl_node *pos; + int idx; + + found = NULL; + idx = au_dr_ihash(ino); + hbl = dr->dr_h_ino + idx; + hlist_bl_lock(hbl); + hlist_bl_for_each_entry(ent, pos, hbl, dr_hnode) + if (ent->dr_h_ino == ino) { + found = ent; + break; + } + hlist_bl_unlock(hbl); + + return found; +} + +int au_dr_hino_test_add(struct au_dr_br *dr, ino_t ino, + struct au_dr_hino *add_ent) +{ + int found, idx; + struct hlist_bl_head *hbl; + struct hlist_bl_node *pos; + struct au_dr_hino *ent; + + found = 0; + idx = au_dr_ihash(ino); + hbl = dr->dr_h_ino + idx; +#if 0 /* debug print */ + { + struct hlist_bl_node *tmp; + + hlist_bl_for_each_entry_safe(ent, pos, tmp, hbl, dr_hnode) + AuDbg("hi%llu\n", (unsigned long long)ent->dr_h_ino); + } +#endif + hlist_bl_lock(hbl); + hlist_bl_for_each_entry(ent, pos, hbl, dr_hnode) + if (ent->dr_h_ino == ino) { + found = 1; + break; + } + if (!found && add_ent) + hlist_bl_add_head(&add_ent->dr_hnode, hbl); + hlist_bl_unlock(hbl); + + if (!found && add_ent) + AuDbg("i%llu added\n", (unsigned long long)add_ent->dr_h_ino); + + return found; +} + +void au_dr_hino_free(struct au_dr_br *dr) +{ + int i; + struct hlist_bl_head *hbl; + struct hlist_bl_node *pos, *tmp; + struct au_dr_hino *ent; + + /* SiMustWriteLock(sb); */ + + for (i = 0; i < AuDirren_NHASH; i++) { + hbl = dr->dr_h_ino + i; + /* no spinlock since sbinfo must be write-locked */ + hlist_bl_for_each_entry_safe(ent, pos, tmp, hbl, dr_hnode) + au_kfree_rcu(ent); + INIT_HLIST_BL_HEAD(hbl); + } +} + +/* returns the number of inodes or an error */ +static int au_dr_hino_store(struct super_block *sb, struct au_branch *br, + struct file *hinofile) +{ + int err, i; + ssize_t ssz; + loff_t pos, oldsize; + __be64 u64; + struct inode *hinoinode; + struct hlist_bl_head *hbl; + struct hlist_bl_node *n1, *n2; + struct au_dr_hino *ent; + + SiMustWriteLock(sb); + AuDebugOn(!au_br_writable(br->br_perm)); + + hinoinode = file_inode(hinofile); + oldsize = i_size_read(hinoinode); + + err = 0; + pos = 0; + hbl = br->br_dirren.dr_h_ino; + for (i = 0; !err && i < AuDirren_NHASH; i++, hbl++) { + /* no bit-lock since sbinfo must be write-locked */ + hlist_bl_for_each_entry_safe(ent, n1, n2, hbl, dr_hnode) { + AuDbg("hi%llu, %pD2\n", + (unsigned long long)ent->dr_h_ino, hinofile); + u64 = cpu_to_be64(ent->dr_h_ino); + ssz = vfsub_write_k(hinofile, &u64, sizeof(u64), &pos); + if (ssz == sizeof(u64)) + continue; + + /* write error */ + pr_err("ssz %zd, %pD2\n", ssz, hinofile); + err = -ENOSPC; + if (ssz < 0) + err = ssz; + break; + } + } + /* regardless the error */ + if (pos < oldsize) { + err = vfsub_trunc(&hinofile->f_path, pos, /*attr*/0, hinofile); + AuTraceErr(err); + } + + AuTraceErr(err); + return err; +} + +static int au_dr_hino_load(struct au_dr_br *dr, struct file *hinofile) +{ + int err, hidx; + ssize_t ssz; + size_t sz, n; + loff_t pos; + uint64_t u64; + struct au_dr_hino *ent; + struct inode *hinoinode; + struct hlist_bl_head *hbl; + + err = 0; + pos = 0; + hbl = dr->dr_h_ino; + hinoinode = file_inode(hinofile); + sz = i_size_read(hinoinode); + AuDebugOn(sz % sizeof(u64)); + n = sz / sizeof(u64); + while (n--) { + ssz = vfsub_read_k(hinofile, &u64, sizeof(u64), &pos); + if (unlikely(ssz != sizeof(u64))) { + pr_err("ssz %zd, %pD2\n", ssz, hinofile); + err = -EINVAL; + if (ssz < 0) + err = ssz; + goto out_free; + } + + ent = kmalloc(sizeof(*ent), GFP_NOFS); + if (!ent) { + err = -ENOMEM; + AuTraceErr(err); + goto out_free; + } + ent->dr_h_ino = be64_to_cpu((__force __be64)u64); + AuDbg("hi%llu, %pD2\n", + (unsigned long long)ent->dr_h_ino, hinofile); + hidx = au_dr_ihash(ent->dr_h_ino); + au_hbl_add(&ent->dr_hnode, hbl + hidx); + } + goto out; /* success */ + +out_free: + au_dr_hino_free(dr); +out: + AuTraceErr(err); + return err; +} + +/* + * @bindex/@br is a switch to distinguish whether suspending hnotify or not. + * @path is a switch to distinguish load and store. + */ +static int au_dr_hino(struct super_block *sb, aufs_bindex_t bindex, + struct au_branch *br, const struct path *path) +{ + int err, flags; + unsigned char load, suspend; + struct file *hinofile; + struct au_hinode *hdir; + struct inode *dir, *delegated; + struct path hinopath; + struct qstr hinoname = QSTR_INIT(AUFS_WH_DR_BRHINO, + sizeof(AUFS_WH_DR_BRHINO) - 1); + + AuDebugOn(bindex < 0 && !br); + AuDebugOn(bindex >= 0 && br); + + err = -EINVAL; + suspend = !br; + if (suspend) + br = au_sbr(sb, bindex); + load = !!path; + if (!load) { + path = &br->br_path; + AuDebugOn(!au_br_writable(br->br_perm)); + if (unlikely(!au_br_writable(br->br_perm))) + goto out; + } + + hdir = NULL; + if (suspend) { + dir = d_inode(sb->s_root); + hdir = au_hinode(au_ii(dir), bindex); + dir = hdir->hi_inode; + au_hn_inode_lock_nested(hdir, AuLsc_I_CHILD); + } else { + dir = d_inode(path->dentry); + inode_lock_nested(dir, AuLsc_I_CHILD); + } + hinopath.mnt = path->mnt; + hinopath.dentry = vfsub_lkup_one(&hinoname, (struct path *)path); + err = PTR_ERR(hinopath.dentry); + if (IS_ERR(hinopath.dentry)) + goto out_unlock; + + err = 0; + flags = O_RDONLY; + if (load) { + if (d_is_negative(hinopath.dentry)) + goto out_dput; /* success */ + } else { + if (au_dr_hino_test_empty(&br->br_dirren)) { + if (d_is_positive(hinopath.dentry)) { + delegated = NULL; + err = vfsub_unlink(dir, &hinopath, &delegated, + /*force*/0); + AuTraceErr(err); + if (unlikely(err)) + pr_err("ignored err %d, %pd2\n", + err, hinopath.dentry); + if (unlikely(err == -EWOULDBLOCK)) + iput(delegated); + err = 0; + } + goto out_dput; + } else if (!d_is_positive(hinopath.dentry)) { + err = vfsub_create(dir, &hinopath, 0600, + /*want_excl*/false); + AuTraceErr(err); + if (unlikely(err)) + goto out_dput; + } + flags = O_WRONLY; + } + hinofile = vfsub_dentry_open(&hinopath, flags); + if (suspend) + au_hn_inode_unlock(hdir); + else + inode_unlock(dir); + dput(hinopath.dentry); + AuTraceErrPtr(hinofile); + if (IS_ERR(hinofile)) { + err = PTR_ERR(hinofile); + goto out; + } + + if (load) + err = au_dr_hino_load(&br->br_dirren, hinofile); + else + err = au_dr_hino_store(sb, br, hinofile); + fput(hinofile); + goto out; + +out_dput: + dput(hinopath.dentry); +out_unlock: + if (suspend) + au_hn_inode_unlock(hdir); + else + inode_unlock(dir); +out: + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int au_dr_brid_init(struct au_dr_brid *brid, const struct path *path) +{ + int err; + struct kstatfs kstfs; + dev_t dev; + struct dentry *dentry; + struct super_block *sb; + + err = vfs_statfs((void *)path, &kstfs); + AuTraceErr(err); + if (unlikely(err)) + goto out; + + /* todo: support for UUID */ + + if (kstfs.f_fsid.val[0] || kstfs.f_fsid.val[1]) { + brid->type = AuBrid_FSID; + brid->fsid = kstfs.f_fsid; + } else { + dentry = path->dentry; + sb = dentry->d_sb; + dev = sb->s_dev; + if (dev) { + brid->type = AuBrid_DEV; + brid->dev = dev; + } + } + +out: + return err; +} + +int au_dr_br_init(struct super_block *sb, struct au_branch *br, + const struct path *path) +{ + int err, i; + struct au_dr_br *dr; + struct hlist_bl_head *hbl; + + dr = &br->br_dirren; + hbl = dr->dr_h_ino; + for (i = 0; i < AuDirren_NHASH; i++, hbl++) + INIT_HLIST_BL_HEAD(hbl); + + err = au_dr_brid_init(&dr->dr_brid, path); + if (unlikely(err)) + goto out; + + if (au_opt_test(au_mntflags(sb), DIRREN)) + err = au_dr_hino(sb, /*bindex*/-1, br, path); + +out: + AuTraceErr(err); + return err; +} + +int au_dr_br_fin(struct super_block *sb, struct au_branch *br) +{ + int err; + + err = 0; + if (au_br_writable(br->br_perm)) + err = au_dr_hino(sb, /*bindex*/-1, br, /*path*/NULL); + if (!err) + au_dr_hino_free(&br->br_dirren); + + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int au_brid_str(struct au_dr_brid *brid, struct inode *h_inode, + char *buf, size_t sz) +{ + int err; + unsigned int major, minor; + char *p; + + p = buf; + err = snprintf(p, sz, "%d_", brid->type); + AuDebugOn(err > sz); + p += err; + sz -= err; + switch (brid->type) { + case AuBrid_Unset: + return -EINVAL; + case AuBrid_UUID: + err = snprintf(p, sz, "%pU", brid->uuid.b); + break; + case AuBrid_FSID: + err = snprintf(p, sz, "%08x-%08x", + brid->fsid.val[0], brid->fsid.val[1]); + break; + case AuBrid_DEV: + major = MAJOR(brid->dev); + minor = MINOR(brid->dev); + if (major <= 0xff && minor <= 0xff) + err = snprintf(p, sz, "%02x%02x", major, minor); + else + err = snprintf(p, sz, "%03x:%05x", major, minor); + break; + } + AuDebugOn(err > sz); + p += err; + sz -= err; + err = snprintf(p, sz, "_%llu", (unsigned long long)h_inode->i_ino); + AuDebugOn(err > sz); + p += err; + sz -= err; + + return p - buf; +} + +static int au_drinfo_name(struct au_branch *br, char *name, int len) +{ + int rlen; + struct dentry *br_dentry; + struct inode *br_inode; + + br_dentry = au_br_dentry(br); + br_inode = d_inode(br_dentry); + rlen = au_brid_str(&br->br_dirren.dr_brid, br_inode, name, len); + AuDebugOn(rlen >= AUFS_DIRREN_ENV_VAL_SZ); + AuDebugOn(rlen > len); + + return rlen; +} + +/* ---------------------------------------------------------------------- */ + +/* + * from the given @h_dentry, construct drinfo at @*fdata. + * when the size of @*fdata is not enough, reallocate and return new @fdata and + * @allocated. + */ +static int au_drinfo_construct(struct au_drinfo_fdata **fdata, + struct dentry *h_dentry, + unsigned char *allocated) +{ + int err, v; + struct au_drinfo_fdata *f, *p; + struct au_drinfo *drinfo; + struct inode *h_inode; + struct qstr *qname; + + err = 0; + f = *fdata; + h_inode = d_inode(h_dentry); + qname = &h_dentry->d_name; + drinfo = &f->drinfo; + drinfo->ino = (__force uint64_t)cpu_to_be64(h_inode->i_ino); + drinfo->oldnamelen = qname->len; + if (*allocated < sizeof(*f) + qname->len) { + v = roundup_pow_of_two(*allocated + qname->len); + p = au_krealloc(f, v, GFP_NOFS, /*may_shrink*/0); + if (unlikely(!p)) { + err = -ENOMEM; + AuTraceErr(err); + goto out; + } + f = p; + *fdata = f; + *allocated = v; + drinfo = &f->drinfo; + } + memcpy(drinfo->oldname, qname->name, qname->len); + AuDbg("i%llu, %.*s\n", + be64_to_cpu((__force __be64)drinfo->ino), drinfo->oldnamelen, + drinfo->oldname); + +out: + AuTraceErr(err); + return err; +} + +/* callers have to free the return value */ +static struct au_drinfo *au_drinfo_read_k(struct file *file, ino_t h_ino) +{ + struct au_drinfo *ret, *drinfo; + struct au_drinfo_fdata fdata; + int len; + loff_t pos; + ssize_t ssz; + + ret = ERR_PTR(-EIO); + pos = 0; + ssz = vfsub_read_k(file, &fdata, sizeof(fdata), &pos); + if (unlikely(ssz != sizeof(fdata))) { + AuIOErr("ssz %zd, %u, %pD2\n", + ssz, (unsigned int)sizeof(fdata), file); + goto out; + } + + fdata.magic = ntohl((__force __be32)fdata.magic); + switch (fdata.magic) { + case AUFS_DRINFO_MAGIC_V1: + break; + default: + AuIOErr("magic-num 0x%x, 0x%x, %pD2\n", + fdata.magic, AUFS_DRINFO_MAGIC_V1, file); + goto out; + } + + drinfo = &fdata.drinfo; + len = drinfo->oldnamelen; + if (!len) { + AuIOErr("broken drinfo %pD2\n", file); + goto out; + } + + ret = NULL; + drinfo->ino = be64_to_cpu((__force __be64)drinfo->ino); + if (unlikely(h_ino && drinfo->ino != h_ino)) { + AuDbg("ignored i%llu, i%llu, %pD2\n", + (unsigned long long)drinfo->ino, + (unsigned long long)h_ino, file); + goto out; /* success */ + } + + ret = kmalloc(sizeof(*ret) + len, GFP_NOFS); + if (unlikely(!ret)) { + ret = ERR_PTR(-ENOMEM); + AuTraceErrPtr(ret); + goto out; + } + + *ret = *drinfo; + ssz = vfsub_read_k(file, (void *)ret->oldname, len, &pos); + if (unlikely(ssz != len)) { + au_kfree_rcu(ret); + ret = ERR_PTR(-EIO); + AuIOErr("ssz %zd, %u, %pD2\n", ssz, len, file); + goto out; + } + + AuDbg("oldname %.*s\n", ret->oldnamelen, ret->oldname); + +out: + return ret; +} + +/* ---------------------------------------------------------------------- */ + +/* in order to be revertible */ +struct au_drinfo_rev_elm { + int created; + struct dentry *info_dentry; + struct au_drinfo *info_last; +}; + +struct au_drinfo_rev { + unsigned char already; + aufs_bindex_t nelm; + struct au_drinfo_rev_elm elm[]; +}; + +/* todo: isn't it too large? */ +struct au_drinfo_store { + struct path h_ppath; + struct dentry *h_dentry; + struct au_drinfo_fdata *fdata; + char *infoname; /* inside of whname, just after PFX */ + char whname[sizeof(AUFS_WH_DR_INFO_PFX) + AUFS_DIRREN_ENV_VAL_SZ]; + aufs_bindex_t btgt, btail; + unsigned char no_sio, + allocated, /* current size of *fdata */ + infonamelen, /* room size for p */ + whnamelen, /* length of the generated name */ + renameback; /* renamed back */ +}; + +/* on rename(2) error, the caller should revert it using @elm */ +static int au_drinfo_do_store(struct au_drinfo_store *w, + struct au_drinfo_rev_elm *elm) +{ + int err, len; + ssize_t ssz; + loff_t pos; + struct path infopath = { + .mnt = w->h_ppath.mnt + }; + struct inode *h_dir, *h_inode, *delegated; + struct file *infofile; + struct qstr *qname; + + AuDebugOn(elm + && memcmp(elm, page_address(ZERO_PAGE(0)), sizeof(*elm))); + + infopath.dentry = vfsub_lookup_one_len(w->whname, &w->h_ppath, + w->whnamelen); + AuTraceErrPtr(infopath.dentry); + if (IS_ERR(infopath.dentry)) { + err = PTR_ERR(infopath.dentry); + goto out; + } + + err = 0; + h_dir = d_inode(w->h_ppath.dentry); + if (elm && d_is_negative(infopath.dentry)) { + err = vfsub_create(h_dir, &infopath, 0600, /*want_excl*/true); + AuTraceErr(err); + if (unlikely(err)) + goto out_dput; + elm->created = 1; + elm->info_dentry = dget(infopath.dentry); + } + + infofile = vfsub_dentry_open(&infopath, O_RDWR); + AuTraceErrPtr(infofile); + if (IS_ERR(infofile)) { + err = PTR_ERR(infofile); + goto out_dput; + } + + h_inode = d_inode(infopath.dentry); + if (elm && i_size_read(h_inode)) { + h_inode = d_inode(w->h_dentry); + elm->info_last = au_drinfo_read_k(infofile, h_inode->i_ino); + AuTraceErrPtr(elm->info_last); + if (IS_ERR(elm->info_last)) { + err = PTR_ERR(elm->info_last); + elm->info_last = NULL; + AuDebugOn(elm->info_dentry); + goto out_fput; + } + } + + if (elm && w->renameback) { + delegated = NULL; + err = vfsub_unlink(h_dir, &infopath, &delegated, /*force*/0); + AuTraceErr(err); + if (unlikely(err == -EWOULDBLOCK)) + iput(delegated); + goto out_fput; + } + + pos = 0; + qname = &w->h_dentry->d_name; + len = sizeof(*w->fdata) + qname->len; + if (!elm) + len = sizeof(*w->fdata) + w->fdata->drinfo.oldnamelen; + ssz = vfsub_write_k(infofile, w->fdata, len, &pos); + if (ssz == len) { + AuDbg("hi%llu, %.*s\n", w->fdata->drinfo.ino, + w->fdata->drinfo.oldnamelen, w->fdata->drinfo.oldname); + goto out_fput; /* success */ + } else { + err = -EIO; + if (ssz < 0) + err = ssz; + /* the caller should revert it using @elm */ + } + +out_fput: + fput(infofile); +out_dput: + dput(infopath.dentry); +out: + AuTraceErr(err); + return err; +} + +struct au_call_drinfo_do_store_args { + int *errp; + struct au_drinfo_store *w; + struct au_drinfo_rev_elm *elm; +}; + +static void au_call_drinfo_do_store(void *args) +{ + struct au_call_drinfo_do_store_args *a = args; + + *a->errp = au_drinfo_do_store(a->w, a->elm); +} + +static int au_drinfo_store_sio(struct au_drinfo_store *w, + struct au_drinfo_rev_elm *elm) +{ + int err, wkq_err; + + if (w->no_sio) + err = au_drinfo_do_store(w, elm); + else { + struct au_call_drinfo_do_store_args a = { + .errp = &err, + .w = w, + .elm = elm + }; + wkq_err = au_wkq_wait(au_call_drinfo_do_store, &a); + if (unlikely(wkq_err)) + err = wkq_err; + } + AuTraceErr(err); + + return err; +} + +static int au_drinfo_store_work_init(struct au_drinfo_store *w, + aufs_bindex_t btgt) +{ + int err; + + memset(w, 0, sizeof(*w)); + w->allocated = roundup_pow_of_two(sizeof(*w->fdata) + 40); + strcpy(w->whname, AUFS_WH_DR_INFO_PFX); + w->infoname = w->whname + sizeof(AUFS_WH_DR_INFO_PFX) - 1; + w->infonamelen = sizeof(w->whname) - sizeof(AUFS_WH_DR_INFO_PFX); + w->btgt = btgt; + w->no_sio = !!uid_eq(current_fsuid(), GLOBAL_ROOT_UID); + + err = -ENOMEM; + w->fdata = kcalloc(1, w->allocated, GFP_NOFS); + if (unlikely(!w->fdata)) { + AuTraceErr(err); + goto out; + } + w->fdata->magic = (__force uint32_t)htonl(AUFS_DRINFO_MAGIC_V1); + err = 0; + +out: + return err; +} + +static void au_drinfo_store_work_fin(struct au_drinfo_store *w) +{ + au_kfree_rcu(w->fdata); +} + +static void au_drinfo_store_rev(struct au_drinfo_rev *rev, + struct au_drinfo_store *w) +{ + struct au_drinfo_rev_elm *elm; + struct inode *h_dir, *delegated; + int err, nelm; + struct path infopath = { + .mnt = w->h_ppath.mnt + }; + + h_dir = d_inode(w->h_ppath.dentry); + IMustLock(h_dir); + + err = 0; + elm = rev->elm; + for (nelm = rev->nelm; nelm > 0; nelm--, elm++) { + AuDebugOn(elm->created && elm->info_last); + if (elm->created) { + AuDbg("here\n"); + delegated = NULL; + infopath.dentry = elm->info_dentry; + err = vfsub_unlink(h_dir, &infopath, &delegated, + !w->no_sio); + AuTraceErr(err); + if (unlikely(err == -EWOULDBLOCK)) + iput(delegated); + dput(elm->info_dentry); + } else if (elm->info_last) { + AuDbg("here\n"); + w->fdata->drinfo = *elm->info_last; + memcpy(w->fdata->drinfo.oldname, + elm->info_last->oldname, + elm->info_last->oldnamelen); + err = au_drinfo_store_sio(w, /*elm*/NULL); + au_kfree_rcu(elm->info_last); + } + if (unlikely(err)) + AuIOErr("%d, %s\n", err, w->whname); + /* go on even if err */ + } +} + +/* caller has to call au_dr_rename_fin() later */ +static int au_drinfo_store(struct dentry *dentry, aufs_bindex_t btgt, + struct qstr *dst_name, void *_rev) +{ + int err, sz, nelm; + aufs_bindex_t bindex, btail; + struct au_drinfo_store work; + struct au_drinfo_rev *rev, **p; + struct au_drinfo_rev_elm *elm; + struct super_block *sb; + struct au_branch *br; + struct au_hinode *hdir; + + err = au_drinfo_store_work_init(&work, btgt); + AuTraceErr(err); + if (unlikely(err)) + goto out; + + err = -ENOMEM; + btail = au_dbtaildir(dentry); + nelm = btail - btgt; + sz = sizeof(*rev) + sizeof(*elm) * nelm; + rev = kcalloc(1, sz, GFP_NOFS); + if (unlikely(!rev)) { + AuTraceErr(err); + goto out_args; + } + rev->nelm = nelm; + elm = rev->elm; + p = _rev; + *p = rev; + + err = 0; + sb = dentry->d_sb; + work.h_ppath.dentry = au_h_dptr(dentry, btgt); + work.h_ppath.mnt = au_sbr_mnt(sb, btgt); + hdir = au_hi(d_inode(dentry), btgt); + au_hn_inode_lock_nested(hdir, AuLsc_I_CHILD); + for (bindex = btgt + 1; bindex <= btail; bindex++, elm++) { + work.h_dentry = au_h_dptr(dentry, bindex); + if (!work.h_dentry) + continue; + + err = au_drinfo_construct(&work.fdata, work.h_dentry, + &work.allocated); + AuTraceErr(err); + if (unlikely(err)) + break; + + work.renameback = au_qstreq(&work.h_dentry->d_name, dst_name); + br = au_sbr(sb, bindex); + work.whnamelen = sizeof(AUFS_WH_DR_INFO_PFX) - 1; + work.whnamelen += au_drinfo_name(br, work.infoname, + work.infonamelen); + AuDbg("whname %.*s, i%llu, %.*s\n", + work.whnamelen, work.whname, + be64_to_cpu((__force __be64)work.fdata->drinfo.ino), + work.fdata->drinfo.oldnamelen, + work.fdata->drinfo.oldname); + + err = au_drinfo_store_sio(&work, elm); + AuTraceErr(err); + if (unlikely(err)) + break; + } + if (unlikely(err)) { + /* revert all drinfo */ + au_drinfo_store_rev(rev, &work); + au_kfree_try_rcu(rev); + *p = NULL; + } + au_hn_inode_unlock(hdir); + +out_args: + au_drinfo_store_work_fin(&work); +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +int au_dr_rename(struct dentry *src, aufs_bindex_t bindex, + struct qstr *dst_name, void *_rev) +{ + int err, already; + ino_t ino; + struct super_block *sb; + struct au_branch *br; + struct au_dr_br *dr; + struct dentry *h_dentry; + struct inode *h_inode; + struct au_dr_hino *ent; + struct au_drinfo_rev *rev, **p; + + AuDbg("bindex %d\n", bindex); + + err = -ENOMEM; + ent = kmalloc(sizeof(*ent), GFP_NOFS); + if (unlikely(!ent)) + goto out; + + sb = src->d_sb; + br = au_sbr(sb, bindex); + dr = &br->br_dirren; + h_dentry = au_h_dptr(src, bindex); + h_inode = d_inode(h_dentry); + ino = h_inode->i_ino; + ent->dr_h_ino = ino; + already = au_dr_hino_test_add(dr, ino, ent); + AuDbg("b%d, hi%llu, already %d\n", + bindex, (unsigned long long)ino, already); + + err = au_drinfo_store(src, bindex, dst_name, _rev); + AuTraceErr(err); + if (!err) { + p = _rev; + rev = *p; + rev->already = already; + goto out; /* success */ + } + + /* revert */ + if (!already) + au_dr_hino_del(dr, ent); + au_kfree_rcu(ent); + +out: + AuTraceErr(err); + return err; +} + +void au_dr_rename_fin(struct dentry *src, aufs_bindex_t btgt, void *_rev) +{ + struct au_drinfo_rev *rev; + struct au_drinfo_rev_elm *elm; + int nelm; + + rev = _rev; + elm = rev->elm; + for (nelm = rev->nelm; nelm > 0; nelm--, elm++) { + dput(elm->info_dentry); + au_kfree_rcu(elm->info_last); + } + au_kfree_try_rcu(rev); +} + +void au_dr_rename_rev(struct dentry *src, aufs_bindex_t btgt, void *_rev) +{ + int err; + struct au_drinfo_store work; + struct au_drinfo_rev *rev = _rev; + struct super_block *sb; + struct au_branch *br; + struct inode *h_inode; + struct au_dr_br *dr; + struct au_dr_hino *ent; + + err = au_drinfo_store_work_init(&work, btgt); + if (unlikely(err)) + goto out; + + sb = src->d_sb; + br = au_sbr(sb, btgt); + work.h_ppath.dentry = au_h_dptr(src, btgt); + work.h_ppath.mnt = au_br_mnt(br); + au_drinfo_store_rev(rev, &work); + au_drinfo_store_work_fin(&work); + if (rev->already) + goto out; + + dr = &br->br_dirren; + h_inode = d_inode(work.h_ppath.dentry); + ent = au_dr_hino_find(dr, h_inode->i_ino); + BUG_ON(!ent); + au_dr_hino_del(dr, ent); + au_kfree_rcu(ent); + +out: + au_kfree_try_rcu(rev); + if (unlikely(err)) + pr_err("failed to remove dirren info\n"); +} + +/* ---------------------------------------------------------------------- */ + +static struct au_drinfo *au_drinfo_do_load(struct path *h_ppath, + char *whname, int whnamelen, + struct dentry **info_dentry) +{ + struct au_drinfo *drinfo; + struct file *f; + struct inode *h_dir; + struct path infopath; + int unlocked; + + AuDbg("%pd/%.*s\n", h_ppath->dentry, whnamelen, whname); + + *info_dentry = NULL; + drinfo = NULL; + unlocked = 0; + h_dir = d_inode(h_ppath->dentry); + inode_lock_shared_nested(h_dir, AuLsc_I_PARENT); + infopath.dentry = vfsub_lookup_one_len(whname, h_ppath, whnamelen); + if (IS_ERR(infopath.dentry)) { + drinfo = (void *)infopath.dentry; + goto out; + } + + if (d_is_negative(infopath.dentry)) + goto out_dput; /* success */ + + infopath.mnt = h_ppath->mnt; + f = vfsub_dentry_open(&infopath, O_RDONLY); + inode_unlock_shared(h_dir); + unlocked = 1; + if (IS_ERR(f)) { + drinfo = (void *)f; + goto out_dput; + } + + drinfo = au_drinfo_read_k(f, /*h_ino*/0); + if (IS_ERR_OR_NULL(drinfo)) + goto out_fput; + + AuDbg("oldname %.*s\n", drinfo->oldnamelen, drinfo->oldname); + *info_dentry = dget(infopath.dentry); /* keep it alive */ + +out_fput: + fput(f); +out_dput: + dput(infopath.dentry); +out: + if (!unlocked) + inode_unlock_shared(h_dir); + AuTraceErrPtr(drinfo); + return drinfo; +} + +struct au_drinfo_do_load_args { + struct au_drinfo **drinfop; + struct path *h_ppath; + char *whname; + int whnamelen; + struct dentry **info_dentry; +}; + +static void au_call_drinfo_do_load(void *args) +{ + struct au_drinfo_do_load_args *a = args; + + *a->drinfop = au_drinfo_do_load(a->h_ppath, a->whname, a->whnamelen, + a->info_dentry); +} + +struct au_drinfo_load { + struct path h_ppath; + struct qstr *qname; + unsigned char no_sio; + + aufs_bindex_t ninfo; + struct au_drinfo **drinfo; +}; + +static int au_drinfo_load(struct au_drinfo_load *w, aufs_bindex_t bindex, + struct au_branch *br) +{ + int err, wkq_err, whnamelen, e; + char whname[sizeof(AUFS_WH_DR_INFO_PFX) + AUFS_DIRREN_ENV_VAL_SZ] + = AUFS_WH_DR_INFO_PFX; + struct au_drinfo *drinfo; + struct qstr oldname; + struct inode *h_dir, *delegated; + struct dentry *info_dentry; + struct path infopath; + + whnamelen = sizeof(AUFS_WH_DR_INFO_PFX) - 1; + whnamelen += au_drinfo_name(br, whname + whnamelen, + sizeof(whname) - whnamelen); + if (w->no_sio) + drinfo = au_drinfo_do_load(&w->h_ppath, whname, whnamelen, + &info_dentry); + else { + struct au_drinfo_do_load_args args = { + .drinfop = &drinfo, + .h_ppath = &w->h_ppath, + .whname = whname, + .whnamelen = whnamelen, + .info_dentry = &info_dentry + }; + wkq_err = au_wkq_wait(au_call_drinfo_do_load, &args); + if (unlikely(wkq_err)) + drinfo = ERR_PTR(wkq_err); + } + err = PTR_ERR(drinfo); + if (IS_ERR_OR_NULL(drinfo)) + goto out; + + err = 0; + oldname.len = drinfo->oldnamelen; + oldname.name = drinfo->oldname; + if (au_qstreq(w->qname, &oldname)) { + /* the name is renamed back */ + au_kfree_rcu(drinfo); + drinfo = NULL; + + infopath.dentry = info_dentry; + infopath.mnt = w->h_ppath.mnt; + h_dir = d_inode(w->h_ppath.dentry); + delegated = NULL; + inode_lock_nested(h_dir, AuLsc_I_PARENT); + e = vfsub_unlink(h_dir, &infopath, &delegated, !w->no_sio); + inode_unlock(h_dir); + if (unlikely(e)) + AuIOErr("ignored %d, %pd2\n", e, &infopath.dentry); + if (unlikely(e == -EWOULDBLOCK)) + iput(delegated); + } + au_kfree_rcu(w->drinfo[bindex]); + w->drinfo[bindex] = drinfo; + dput(info_dentry); + +out: + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static void au_dr_lkup_free(struct au_drinfo **drinfo, int n) +{ + struct au_drinfo **p = drinfo; + + while (n-- > 0) + au_kfree_rcu(*drinfo++); + au_kfree_try_rcu(p); +} + +int au_dr_lkup(struct au_do_lookup_args *lkup, struct dentry *dentry, + aufs_bindex_t btgt) +{ + int err, ninfo; + struct au_drinfo_load w; + aufs_bindex_t bindex, bbot; + struct au_branch *br; + struct inode *h_dir; + struct au_dr_hino *ent; + struct super_block *sb; + + AuDbg("%.*s, name %.*s, whname %.*s, b%d\n", + AuLNPair(&dentry->d_name), AuLNPair(&lkup->dirren.dr_name), + AuLNPair(&lkup->whname), btgt); + + sb = dentry->d_sb; + bbot = au_sbbot(sb); + w.ninfo = bbot + 1; + if (!lkup->dirren.drinfo) { + lkup->dirren.drinfo = kcalloc(w.ninfo, + sizeof(*lkup->dirren.drinfo), + GFP_NOFS); + if (unlikely(!lkup->dirren.drinfo)) { + err = -ENOMEM; + goto out; + } + lkup->dirren.ninfo = w.ninfo; + } + w.drinfo = lkup->dirren.drinfo; + w.no_sio = !!uid_eq(current_fsuid(), GLOBAL_ROOT_UID); + w.h_ppath.dentry = au_h_dptr(dentry, btgt); + AuDebugOn(!w.h_ppath.dentry); + w.h_ppath.mnt = au_sbr_mnt(sb, btgt); + w.qname = &dentry->d_name; + + ninfo = 0; + for (bindex = btgt + 1; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + err = au_drinfo_load(&w, bindex, br); + if (unlikely(err)) + goto out_free; + if (w.drinfo[bindex]) + ninfo++; + } + if (!ninfo) { + br = au_sbr(sb, btgt); + h_dir = d_inode(w.h_ppath.dentry); + ent = au_dr_hino_find(&br->br_dirren, h_dir->i_ino); + AuDebugOn(!ent); + au_dr_hino_del(&br->br_dirren, ent); + au_kfree_rcu(ent); + } + goto out; /* success */ + +out_free: + au_dr_lkup_free(lkup->dirren.drinfo, lkup->dirren.ninfo); + lkup->dirren.ninfo = 0; + lkup->dirren.drinfo = NULL; +out: + AuTraceErr(err); + return err; +} + +void au_dr_lkup_fin(struct au_do_lookup_args *lkup) +{ + au_dr_lkup_free(lkup->dirren.drinfo, lkup->dirren.ninfo); +} + +int au_dr_lkup_name(struct au_do_lookup_args *lkup, aufs_bindex_t btgt) +{ + int err; + struct au_drinfo *drinfo; + + err = 0; + if (!lkup->dirren.drinfo) + goto out; + AuDebugOn(lkup->dirren.ninfo <= btgt); + drinfo = lkup->dirren.drinfo[btgt]; + if (!drinfo) + goto out; + + au_kfree_try_rcu(lkup->whname.name); + lkup->whname.name = NULL; + lkup->dirren.dr_name.len = drinfo->oldnamelen; + lkup->dirren.dr_name.name = drinfo->oldname; + lkup->name = &lkup->dirren.dr_name; + err = au_wh_name_alloc(&lkup->whname, lkup->name); + if (!err) + AuDbg("name %.*s, whname %.*s, b%d\n", + AuLNPair(lkup->name), AuLNPair(&lkup->whname), + btgt); + +out: + AuTraceErr(err); + return err; +} + +int au_dr_lkup_h_ino(struct au_do_lookup_args *lkup, aufs_bindex_t bindex, + ino_t h_ino) +{ + int match; + struct au_drinfo *drinfo; + + match = 1; + if (!lkup->dirren.drinfo) + goto out; + AuDebugOn(lkup->dirren.ninfo <= bindex); + drinfo = lkup->dirren.drinfo[bindex]; + if (!drinfo) + goto out; + + match = (drinfo->ino == h_ino); + AuDbg("match %d\n", match); + +out: + return match; +} + +/* ---------------------------------------------------------------------- */ + +int au_dr_opt_set(struct super_block *sb) +{ + int err; + aufs_bindex_t bindex, bbot; + struct au_branch *br; + + err = 0; + bbot = au_sbbot(sb); + for (bindex = 0; !err && bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + err = au_dr_hino(sb, bindex, /*br*/NULL, &br->br_path); + } + + return err; +} + +int au_dr_opt_flush(struct super_block *sb) +{ + int err; + aufs_bindex_t bindex, bbot; + struct au_branch *br; + + err = 0; + bbot = au_sbbot(sb); + for (bindex = 0; !err && bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + if (au_br_writable(br->br_perm)) + err = au_dr_hino(sb, bindex, /*br*/NULL, /*path*/NULL); + } + + return err; +} + +int au_dr_opt_clr(struct super_block *sb, int no_flush) +{ + int err; + aufs_bindex_t bindex, bbot; + struct au_branch *br; + + err = 0; + if (!no_flush) { + err = au_dr_opt_flush(sb); + if (unlikely(err)) + goto out; + } + + bbot = au_sbbot(sb); + for (bindex = 0; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + au_dr_hino_free(&br->br_dirren); + } + +out: + return err; +} diff --git a/fs/aufs/dirren.h b/fs/aufs/dirren.h new file mode 100644 index 0000000000000..2c96cf8b039e7 --- /dev/null +++ b/fs/aufs/dirren.h @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2017-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * renamed dir info + */ + +#ifndef __AUFS_DIRREN_H__ +#define __AUFS_DIRREN_H__ + +#ifdef __KERNEL__ + +#include +#include +#include +#include "hbl.h" + +#define AuDirren_NHASH 100 + +#ifdef CONFIG_AUFS_DIRREN +enum au_brid_type { + AuBrid_Unset, + AuBrid_UUID, + AuBrid_FSID, + AuBrid_DEV +}; + +struct au_dr_brid { + enum au_brid_type type; + union { + uuid_t uuid; /* unimplemented yet */ + fsid_t fsid; + dev_t dev; + }; +}; + +/* 20 is the max digits length of ulong 64 */ +/* brid-type "_" uuid "_" inum */ +#define AUFS_DIRREN_FNAME_SZ (1 + 1 + UUID_STRING_LEN + 20) +#define AUFS_DIRREN_ENV_VAL_SZ (AUFS_DIRREN_FNAME_SZ + 1 + 20) + +struct au_dr_hino { + struct hlist_bl_node dr_hnode; + ino_t dr_h_ino; +}; + +struct au_dr_br { + struct hlist_bl_head dr_h_ino[AuDirren_NHASH]; + struct au_dr_brid dr_brid; +}; + +struct au_dr_lookup { + /* dr_name is pointed by struct au_do_lookup_args.name */ + struct qstr dr_name; /* subset of dr_info */ + aufs_bindex_t ninfo; + struct au_drinfo **drinfo; +}; +#else +struct au_dr_hino; +/* empty */ +struct au_dr_br { }; +struct au_dr_lookup { }; +#endif + +/* ---------------------------------------------------------------------- */ + +struct au_branch; +struct au_do_lookup_args; +struct au_hinode; +#ifdef CONFIG_AUFS_DIRREN +int au_dr_hino_test_add(struct au_dr_br *dr, ino_t h_ino, + struct au_dr_hino *add_ent); +void au_dr_hino_free(struct au_dr_br *dr); +int au_dr_br_init(struct super_block *sb, struct au_branch *br, + const struct path *path); +int au_dr_br_fin(struct super_block *sb, struct au_branch *br); +int au_dr_rename(struct dentry *src, aufs_bindex_t bindex, + struct qstr *dst_name, void *_rev); +void au_dr_rename_fin(struct dentry *src, aufs_bindex_t btgt, void *rev); +void au_dr_rename_rev(struct dentry *src, aufs_bindex_t bindex, void *rev); +int au_dr_lkup(struct au_do_lookup_args *lkup, struct dentry *dentry, + aufs_bindex_t bindex); +int au_dr_lkup_name(struct au_do_lookup_args *lkup, aufs_bindex_t btgt); +int au_dr_lkup_h_ino(struct au_do_lookup_args *lkup, aufs_bindex_t bindex, + ino_t h_ino); +void au_dr_lkup_fin(struct au_do_lookup_args *lkup); +int au_dr_opt_set(struct super_block *sb); +int au_dr_opt_flush(struct super_block *sb); +int au_dr_opt_clr(struct super_block *sb, int no_flush); +#else +AuStubInt0(au_dr_hino_test_add, struct au_dr_br *dr, ino_t h_ino, + struct au_dr_hino *add_ent); +AuStubVoid(au_dr_hino_free, struct au_dr_br *dr); +AuStubInt0(au_dr_br_init, struct super_block *sb, struct au_branch *br, + const struct path *path); +AuStubInt0(au_dr_br_fin, struct super_block *sb, struct au_branch *br); +AuStubInt0(au_dr_rename, struct dentry *src, aufs_bindex_t bindex, + struct qstr *dst_name, void *_rev); +AuStubVoid(au_dr_rename_fin, struct dentry *src, aufs_bindex_t btgt, void *rev); +AuStubVoid(au_dr_rename_rev, struct dentry *src, aufs_bindex_t bindex, + void *rev); +AuStubInt0(au_dr_lkup, struct au_do_lookup_args *lkup, struct dentry *dentry, + aufs_bindex_t bindex); +AuStubInt0(au_dr_lkup_name, struct au_do_lookup_args *lkup, aufs_bindex_t btgt); +AuStubInt0(au_dr_lkup_h_ino, struct au_do_lookup_args *lkup, + aufs_bindex_t bindex, ino_t h_ino); +AuStubVoid(au_dr_lkup_fin, struct au_do_lookup_args *lkup); +AuStubInt0(au_dr_opt_set, struct super_block *sb); +AuStubInt0(au_dr_opt_flush, struct super_block *sb); +AuStubInt0(au_dr_opt_clr, struct super_block *sb, int no_flush); +#endif + +/* ---------------------------------------------------------------------- */ + +#ifdef CONFIG_AUFS_DIRREN +static inline int au_dr_ihash(ino_t h_ino) +{ + return h_ino % AuDirren_NHASH; +} +#else +AuStubInt0(au_dr_ihash, ino_t h_ino); +#endif + +#endif /* __KERNEL__ */ +#endif /* __AUFS_DIRREN_H__ */ diff --git a/fs/aufs/dynop.c b/fs/aufs/dynop.c new file mode 100644 index 0000000000000..a84d986307e50 --- /dev/null +++ b/fs/aufs/dynop.c @@ -0,0 +1,368 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2010-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * dynamically customizable operations for regular files + */ + +#include "aufs.h" + +#define DyPrSym(key) AuDbgSym(key->dk_op.dy_hop) + +/* + * How large will these lists be? + * Usually just a few elements, 20-30 at most for each, I guess. + */ +static struct hlist_bl_head dynop[AuDyLast]; + +static struct au_dykey *dy_gfind_get(struct hlist_bl_head *hbl, + const void *h_op) +{ + struct au_dykey *key, *tmp; + struct hlist_bl_node *pos; + + key = NULL; + hlist_bl_lock(hbl); + hlist_bl_for_each_entry(tmp, pos, hbl, dk_hnode) + if (tmp->dk_op.dy_hop == h_op) { + if (kref_get_unless_zero(&tmp->dk_kref)) + key = tmp; + break; + } + hlist_bl_unlock(hbl); + + return key; +} + +static struct au_dykey *dy_bradd(struct au_branch *br, struct au_dykey *key) +{ + struct au_dykey **k, *found; + const void *h_op = key->dk_op.dy_hop; + int i; + + found = NULL; + k = br->br_dykey; + for (i = 0; i < AuBrDynOp; i++) + if (k[i]) { + if (k[i]->dk_op.dy_hop == h_op) { + found = k[i]; + break; + } + } else + break; + if (!found) { + spin_lock(&br->br_dykey_lock); + for (; i < AuBrDynOp; i++) + if (k[i]) { + if (k[i]->dk_op.dy_hop == h_op) { + found = k[i]; + break; + } + } else { + k[i] = key; + break; + } + spin_unlock(&br->br_dykey_lock); + BUG_ON(i == AuBrDynOp); /* expand the array */ + } + + return found; +} + +/* kref_get() if @key is already added */ +static struct au_dykey *dy_gadd(struct hlist_bl_head *hbl, struct au_dykey *key) +{ + struct au_dykey *tmp, *found; + struct hlist_bl_node *pos; + const void *h_op = key->dk_op.dy_hop; + + found = NULL; + hlist_bl_lock(hbl); + hlist_bl_for_each_entry(tmp, pos, hbl, dk_hnode) + if (tmp->dk_op.dy_hop == h_op) { + if (kref_get_unless_zero(&tmp->dk_kref)) + found = tmp; + break; + } + if (!found) + hlist_bl_add_head(&key->dk_hnode, hbl); + hlist_bl_unlock(hbl); + + if (!found) + DyPrSym(key); + return found; +} + +static void dy_free_rcu(struct rcu_head *rcu) +{ + struct au_dykey *key; + + key = container_of(rcu, struct au_dykey, dk_rcu); + DyPrSym(key); + kfree(key); +} + +static void dy_free(struct kref *kref) +{ + struct au_dykey *key; + struct hlist_bl_head *hbl; + + key = container_of(kref, struct au_dykey, dk_kref); + hbl = dynop + key->dk_op.dy_type; + au_hbl_del(&key->dk_hnode, hbl); + call_rcu(&key->dk_rcu, dy_free_rcu); +} + +void au_dy_put(struct au_dykey *key) +{ + kref_put(&key->dk_kref, dy_free); +} + +/* ---------------------------------------------------------------------- */ + +#define DyDbgSize(cnt, op) AuDebugOn(cnt != sizeof(op)/sizeof(void *)) + +#ifdef CONFIG_AUFS_DEBUG +#define DyDbgDeclare(cnt) unsigned int cnt = 0 +#define DyDbgInc(cnt) do { cnt++; } while (0) +#else +#define DyDbgDeclare(cnt) do {} while (0) +#define DyDbgInc(cnt) do {} while (0) +#endif + +#define DySet(func, dst, src, h_op, h_sb) do { \ + DyDbgInc(cnt); \ + if (h_op->func) { \ + if (src.func) \ + dst.func = src.func; \ + else \ + AuDbg("%s %s\n", au_sbtype(h_sb), #func); \ + } \ +} while (0) + +#define DySetForce(func, dst, src) do { \ + AuDebugOn(!src.func); \ + DyDbgInc(cnt); \ + dst.func = src.func; \ +} while (0) + +#define DySetAop(func) \ + DySet(func, dyaop->da_op, aufs_aop, h_aop, h_sb) +#define DySetAopForce(func) \ + DySetForce(func, dyaop->da_op, aufs_aop) + +static void dy_aop(struct au_dykey *key, const void *h_op, + struct super_block *h_sb __maybe_unused) +{ + struct au_dyaop *dyaop = (void *)key; + const struct address_space_operations *h_aop = h_op; + DyDbgDeclare(cnt); + + AuDbg("%s\n", au_sbtype(h_sb)); + + DySetAop(writepage); + DySetAopForce(readpage); /* force */ + DySetAop(writepages); + DySetAop(set_page_dirty); + DySetAop(readpages); + DySetAop(readahead); + DySetAop(write_begin); + DySetAop(write_end); + DySetAop(bmap); + DySetAop(invalidatepage); + DySetAop(releasepage); + DySetAop(freepage); + /* this one will be changed according to an aufs mount option */ + DySetAop(direct_IO); + DySetAop(migratepage); + DySetAop(isolate_page); + DySetAop(putback_page); + DySetAop(launder_page); + DySetAop(is_partially_uptodate); + DySetAop(is_dirty_writeback); + DySetAop(error_remove_page); + DySetAop(swap_activate); + DySetAop(swap_deactivate); + + DyDbgSize(cnt, *h_aop); +} + +/* ---------------------------------------------------------------------- */ + +static void dy_bug(struct kref *kref) +{ + BUG(); +} + +static struct au_dykey *dy_get(struct au_dynop *op, struct au_branch *br) +{ + struct au_dykey *key, *old; + struct hlist_bl_head *hbl; + struct op { + unsigned int sz; + void (*set)(struct au_dykey *key, const void *h_op, + struct super_block *h_sb __maybe_unused); + }; + static const struct op a[] = { + [AuDy_AOP] = { + .sz = sizeof(struct au_dyaop), + .set = dy_aop + } + }; + const struct op *p; + + hbl = dynop + op->dy_type; + key = dy_gfind_get(hbl, op->dy_hop); + if (key) + goto out_add; /* success */ + + p = a + op->dy_type; + key = kzalloc(p->sz, GFP_NOFS); + if (unlikely(!key)) { + key = ERR_PTR(-ENOMEM); + goto out; + } + + key->dk_op.dy_hop = op->dy_hop; + kref_init(&key->dk_kref); + p->set(key, op->dy_hop, au_br_sb(br)); + old = dy_gadd(hbl, key); + if (old) { + au_kfree_rcu(key); + key = old; + } + +out_add: + old = dy_bradd(br, key); + if (old) + /* its ref-count should never be zero here */ + kref_put(&key->dk_kref, dy_bug); +out: + return key; +} + +/* ---------------------------------------------------------------------- */ +/* + * Aufs prohibits O_DIRECT by default even if the branch supports it. + * This behaviour is necessary to return an error from open(O_DIRECT) instead + * of the succeeding I/O. The dio mount option enables O_DIRECT and makes + * open(O_DIRECT) always succeed, but the succeeding I/O may return an error. + * See the aufs manual in detail. + */ +static void dy_adx(struct au_dyaop *dyaop, int do_dx) +{ + if (!do_dx) + dyaop->da_op.direct_IO = NULL; + else + dyaop->da_op.direct_IO = aufs_aop.direct_IO; +} + +static struct au_dyaop *dy_aget(struct au_branch *br, + const struct address_space_operations *h_aop, + int do_dx) +{ + struct au_dyaop *dyaop; + struct au_dynop op; + + op.dy_type = AuDy_AOP; + op.dy_haop = h_aop; + dyaop = (void *)dy_get(&op, br); + if (IS_ERR(dyaop)) + goto out; + dy_adx(dyaop, do_dx); + +out: + return dyaop; +} + +int au_dy_iaop(struct inode *inode, aufs_bindex_t bindex, + struct inode *h_inode) +{ + int err, do_dx; + struct super_block *sb; + struct au_branch *br; + struct au_dyaop *dyaop; + + AuDebugOn(!S_ISREG(h_inode->i_mode)); + IiMustWriteLock(inode); + + sb = inode->i_sb; + br = au_sbr(sb, bindex); + do_dx = !!au_opt_test(au_mntflags(sb), DIO); + dyaop = dy_aget(br, h_inode->i_mapping->a_ops, do_dx); + err = PTR_ERR(dyaop); + if (IS_ERR(dyaop)) + /* unnecessary to call dy_fput() */ + goto out; + + err = 0; + inode->i_mapping->a_ops = &dyaop->da_op; + +out: + return err; +} + +/* + * Is it safe to replace a_ops during the inode/file is in operation? + * Yes, I hope so. + */ +int au_dy_irefresh(struct inode *inode) +{ + int err; + aufs_bindex_t btop; + struct inode *h_inode; + + err = 0; + if (S_ISREG(inode->i_mode)) { + btop = au_ibtop(inode); + h_inode = au_h_iptr(inode, btop); + err = au_dy_iaop(inode, btop, h_inode); + } + return err; +} + +void au_dy_arefresh(int do_dx) +{ + struct hlist_bl_head *hbl; + struct hlist_bl_node *pos; + struct au_dykey *key; + + hbl = dynop + AuDy_AOP; + hlist_bl_lock(hbl); + hlist_bl_for_each_entry(key, pos, hbl, dk_hnode) + dy_adx((void *)key, do_dx); + hlist_bl_unlock(hbl); +} + +/* ---------------------------------------------------------------------- */ + +void __init au_dy_init(void) +{ + int i; + + for (i = 0; i < AuDyLast; i++) + INIT_HLIST_BL_HEAD(dynop + i); +} + +void au_dy_fin(void) +{ + int i; + + for (i = 0; i < AuDyLast; i++) + WARN_ON(!hlist_bl_empty(dynop + i)); +} diff --git a/fs/aufs/dynop.h b/fs/aufs/dynop.h new file mode 100644 index 0000000000000..a86b106b2a138 --- /dev/null +++ b/fs/aufs/dynop.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2010-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * dynamically customizable operations (for regular files only) + */ + +#ifndef __AUFS_DYNOP_H__ +#define __AUFS_DYNOP_H__ + +#ifdef __KERNEL__ + +#include +#include + +enum {AuDy_AOP, AuDyLast}; + +struct au_dynop { + int dy_type; + union { + const void *dy_hop; + const struct address_space_operations *dy_haop; + }; +}; + +struct au_dykey { + union { + struct hlist_bl_node dk_hnode; + struct rcu_head dk_rcu; + }; + struct au_dynop dk_op; + + /* + * during I am in the branch local array, kref is gotten. when the + * branch is removed, kref is put. + */ + struct kref dk_kref; +}; + +/* stop unioning since their sizes are very different from each other */ +struct au_dyaop { + struct au_dykey da_key; + struct address_space_operations da_op; /* not const */ +}; +/* make sure that 'struct au_dykey *' can be any type */ +static_assert(!offsetof(struct au_dyaop, da_key)); + +/* ---------------------------------------------------------------------- */ + +/* dynop.c */ +struct au_branch; +void au_dy_put(struct au_dykey *key); +int au_dy_iaop(struct inode *inode, aufs_bindex_t bindex, + struct inode *h_inode); +int au_dy_irefresh(struct inode *inode); +void au_dy_arefresh(int do_dio); + +void __init au_dy_init(void); +void au_dy_fin(void); + +#endif /* __KERNEL__ */ +#endif /* __AUFS_DYNOP_H__ */ diff --git a/fs/aufs/export.c b/fs/aufs/export.c new file mode 100644 index 0000000000000..4cb44cdc47eff --- /dev/null +++ b/fs/aufs/export.c @@ -0,0 +1,836 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * export via nfs + */ + +#include +#include +#include +#include +#include +#include "aufs.h" + +union conv { +#ifdef CONFIG_AUFS_INO_T_64 + __u32 a[2]; +#else + __u32 a[1]; +#endif + ino_t ino; +}; + +static ino_t decode_ino(__u32 *a) +{ + union conv u; + + BUILD_BUG_ON(sizeof(u.ino) != sizeof(u.a)); + u.a[0] = a[0]; +#ifdef CONFIG_AUFS_INO_T_64 + u.a[1] = a[1]; +#endif + return u.ino; +} + +static void encode_ino(__u32 *a, ino_t ino) +{ + union conv u; + + u.ino = ino; + a[0] = u.a[0]; +#ifdef CONFIG_AUFS_INO_T_64 + a[1] = u.a[1]; +#endif +} + +/* NFS file handle */ +enum { + Fh_br_id, + Fh_sigen, +#ifdef CONFIG_AUFS_INO_T_64 + /* support 64bit inode number */ + Fh_ino1, + Fh_ino2, + Fh_dir_ino1, + Fh_dir_ino2, +#else + Fh_ino1, + Fh_dir_ino1, +#endif + Fh_igen, + Fh_h_type, + Fh_tail, + + Fh_ino = Fh_ino1, + Fh_dir_ino = Fh_dir_ino1 +}; + +static int au_test_anon(struct dentry *dentry) +{ + /* note: read d_flags without d_lock */ + return !!(dentry->d_flags & DCACHE_DISCONNECTED); +} + +int au_test_nfsd(void) +{ + int ret; + struct task_struct *tsk = current; + char comm[sizeof(tsk->comm)]; + + ret = 0; + if (tsk->flags & PF_KTHREAD) { + get_task_comm(comm, tsk); + ret = !strcmp(comm, "nfsd"); + } + + return ret; +} + +/* ---------------------------------------------------------------------- */ +/* inode generation external table */ + +void au_xigen_inc(struct inode *inode) +{ + loff_t pos; + ssize_t sz; + __u32 igen; + struct super_block *sb; + struct au_sbinfo *sbinfo; + + sb = inode->i_sb; + AuDebugOn(!au_opt_test(au_mntflags(sb), XINO)); + + sbinfo = au_sbi(sb); + pos = inode->i_ino; + pos *= sizeof(igen); + igen = inode->i_generation + 1; + sz = xino_fwrite(sbinfo->si_xigen, &igen, sizeof(igen), &pos); + if (sz == sizeof(igen)) + return; /* success */ + + if (unlikely(sz >= 0)) + AuIOErr("xigen error (%zd)\n", sz); +} + +int au_xigen_new(struct inode *inode) +{ + int err; + loff_t pos; + ssize_t sz; + struct super_block *sb; + struct au_sbinfo *sbinfo; + struct file *file; + + err = 0; + /* todo: dirty, at mount time */ + if (inode->i_ino == AUFS_ROOT_INO) + goto out; + sb = inode->i_sb; + SiMustAnyLock(sb); + if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) + goto out; + + err = -EFBIG; + pos = inode->i_ino; + if (unlikely(au_loff_max / sizeof(inode->i_generation) - 1 < pos)) { + AuIOErr1("too large i%lld\n", pos); + goto out; + } + pos *= sizeof(inode->i_generation); + + err = 0; + sbinfo = au_sbi(sb); + file = sbinfo->si_xigen; + BUG_ON(!file); + + if (vfsub_f_size_read(file) + < pos + sizeof(inode->i_generation)) { + inode->i_generation = atomic_inc_return(&sbinfo->si_xigen_next); + sz = xino_fwrite(file, &inode->i_generation, + sizeof(inode->i_generation), &pos); + } else + sz = xino_fread(file, &inode->i_generation, + sizeof(inode->i_generation), &pos); + if (sz == sizeof(inode->i_generation)) + goto out; /* success */ + + err = sz; + if (unlikely(sz >= 0)) { + err = -EIO; + AuIOErr("xigen error (%zd)\n", sz); + } + +out: + return err; +} + +int au_xigen_set(struct super_block *sb, struct path *path) +{ + int err; + struct au_sbinfo *sbinfo; + struct file *file; + + SiMustWriteLock(sb); + + sbinfo = au_sbi(sb); + file = au_xino_create2(sb, path, sbinfo->si_xigen); + err = PTR_ERR(file); + if (IS_ERR(file)) + goto out; + err = 0; + if (sbinfo->si_xigen) + fput(sbinfo->si_xigen); + sbinfo->si_xigen = file; + +out: + AuTraceErr(err); + return err; +} + +void au_xigen_clr(struct super_block *sb) +{ + struct au_sbinfo *sbinfo; + + SiMustWriteLock(sb); + + sbinfo = au_sbi(sb); + if (sbinfo->si_xigen) { + fput(sbinfo->si_xigen); + sbinfo->si_xigen = NULL; + } +} + +/* ---------------------------------------------------------------------- */ + +static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino, + ino_t dir_ino) +{ + struct dentry *dentry, *d; + struct inode *inode; + unsigned int sigen; + + dentry = NULL; + inode = ilookup(sb, ino); + if (!inode) + goto out; + + dentry = ERR_PTR(-ESTALE); + sigen = au_sigen(sb); + if (unlikely(au_is_bad_inode(inode) + || IS_DEADDIR(inode) + || sigen != au_iigen(inode, NULL))) + goto out_iput; + + dentry = NULL; + if (!dir_ino || S_ISDIR(inode->i_mode)) + dentry = d_find_alias(inode); + else { + spin_lock(&inode->i_lock); + hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) { + spin_lock(&d->d_lock); + if (!au_test_anon(d) + && d_inode(d->d_parent)->i_ino == dir_ino) { + dentry = dget_dlock(d); + spin_unlock(&d->d_lock); + break; + } + spin_unlock(&d->d_lock); + } + spin_unlock(&inode->i_lock); + } + if (unlikely(dentry && au_digen_test(dentry, sigen))) { + /* need to refresh */ + dput(dentry); + dentry = NULL; + } + +out_iput: + iput(inode); +out: + AuTraceErrPtr(dentry); + return dentry; +} + +/* ---------------------------------------------------------------------- */ + +/* todo: dirty? */ +/* if exportfs_decode_fh() passed vfsmount*, we could be happy */ + +struct au_compare_mnt_args { + /* input */ + struct super_block *sb; + + /* output */ + struct vfsmount *mnt; +}; + +static int au_compare_mnt(struct vfsmount *mnt, void *arg) +{ + struct au_compare_mnt_args *a = arg; + + if (mnt->mnt_sb != a->sb) + return 0; + a->mnt = mntget(mnt); + return 1; +} + +static struct vfsmount *au_mnt_get(struct super_block *sb) +{ + int err; + struct path root; + struct au_compare_mnt_args args = { + .sb = sb + }; + + get_fs_root(current->fs, &root); + rcu_read_lock(); + err = iterate_mounts(au_compare_mnt, &args, root.mnt); + rcu_read_unlock(); + path_put(&root); + AuDebugOn(!err); + AuDebugOn(!args.mnt); + return args.mnt; +} + +struct au_nfsd_si_lock { + unsigned int sigen; + aufs_bindex_t bindex, br_id; + unsigned char force_lock; +}; + +static int si_nfsd_read_lock(struct super_block *sb, + struct au_nfsd_si_lock *nsi_lock) +{ + int err; + aufs_bindex_t bindex; + + si_read_lock(sb, AuLock_FLUSH); + + /* branch id may be wrapped around */ + err = 0; + bindex = au_br_index(sb, nsi_lock->br_id); + if (bindex >= 0 && nsi_lock->sigen + AUFS_BRANCH_MAX > au_sigen(sb)) + goto out; /* success */ + + err = -ESTALE; + bindex = -1; + if (!nsi_lock->force_lock) + si_read_unlock(sb); + +out: + nsi_lock->bindex = bindex; + return err; +} + +struct find_name_by_ino { + struct dir_context ctx; + int called, found; + ino_t ino; + char *name; + int namelen; +}; + +static int +find_name_by_ino(struct dir_context *ctx, const char *name, int namelen, + loff_t offset, u64 ino, unsigned int d_type) +{ + struct find_name_by_ino *a = container_of(ctx, struct find_name_by_ino, + ctx); + + a->called++; + if (a->ino != ino) + return 0; + + memcpy(a->name, name, namelen); + a->namelen = namelen; + a->found = 1; + return 1; +} + +static struct dentry *au_lkup_by_ino(struct path *path, ino_t ino, + struct au_nfsd_si_lock *nsi_lock) +{ + struct dentry *dentry, *parent; + struct file *file; + struct inode *dir; + struct find_name_by_ino arg = { + .ctx = { + .actor = find_name_by_ino + } + }; + int err; + + parent = path->dentry; + if (nsi_lock) + si_read_unlock(parent->d_sb); + file = vfsub_dentry_open(path, au_dir_roflags); + dentry = (void *)file; + if (IS_ERR(file)) + goto out; + + dentry = ERR_PTR(-ENOMEM); + arg.name = (void *)__get_free_page(GFP_NOFS); + if (unlikely(!arg.name)) + goto out_file; + arg.ino = ino; + arg.found = 0; + do { + arg.called = 0; + /* smp_mb(); */ + err = vfsub_iterate_dir(file, &arg.ctx); + } while (!err && !arg.found && arg.called); + dentry = ERR_PTR(err); + if (unlikely(err)) + goto out_name; + /* instead of ENOENT */ + dentry = ERR_PTR(-ESTALE); + if (!arg.found) + goto out_name; + + /* do not call vfsub_lkup_one() */ + dir = d_inode(parent); + dentry = vfsub_lookup_one_len_unlocked(arg.name, path, arg.namelen); + AuTraceErrPtr(dentry); + if (IS_ERR(dentry)) + goto out_name; + AuDebugOn(au_test_anon(dentry)); + if (unlikely(d_really_is_negative(dentry))) { + dput(dentry); + dentry = ERR_PTR(-ENOENT); + } + +out_name: + free_page((unsigned long)arg.name); +out_file: + fput(file); +out: + if (unlikely(nsi_lock + && si_nfsd_read_lock(parent->d_sb, nsi_lock) < 0)) + if (!IS_ERR(dentry)) { + dput(dentry); + dentry = ERR_PTR(-ESTALE); + } + AuTraceErrPtr(dentry); + return dentry; +} + +static struct dentry *decode_by_dir_ino(struct super_block *sb, ino_t ino, + ino_t dir_ino, + struct au_nfsd_si_lock *nsi_lock) +{ + struct dentry *dentry; + struct path path; + + if (dir_ino != AUFS_ROOT_INO) { + path.dentry = decode_by_ino(sb, dir_ino, 0); + dentry = path.dentry; + if (!path.dentry || IS_ERR(path.dentry)) + goto out; + AuDebugOn(au_test_anon(path.dentry)); + } else + path.dentry = dget(sb->s_root); + + path.mnt = au_mnt_get(sb); + dentry = au_lkup_by_ino(&path, ino, nsi_lock); + path_put(&path); + +out: + AuTraceErrPtr(dentry); + return dentry; +} + +/* ---------------------------------------------------------------------- */ + +static int h_acceptable(void *expv, struct dentry *dentry) +{ + return 1; +} + +static char *au_build_path(struct dentry *h_parent, struct path *h_rootpath, + char *buf, int len, struct super_block *sb) +{ + char *p; + int n; + struct path path; + + p = d_path(h_rootpath, buf, len); + if (IS_ERR(p)) + goto out; + n = strlen(p); + + path.mnt = h_rootpath->mnt; + path.dentry = h_parent; + p = d_path(&path, buf, len); + if (IS_ERR(p)) + goto out; + if (n != 1) + p += n; + + path.mnt = au_mnt_get(sb); + path.dentry = sb->s_root; + p = d_path(&path, buf, len - strlen(p)); + mntput(path.mnt); + if (IS_ERR(p)) + goto out; + if (n != 1) + p[strlen(p)] = '/'; + +out: + AuTraceErrPtr(p); + return p; +} + +static +struct dentry *decode_by_path(struct super_block *sb, ino_t ino, __u32 *fh, + int fh_len, struct au_nfsd_si_lock *nsi_lock) +{ + struct dentry *dentry, *h_parent, *root; + struct super_block *h_sb; + char *pathname, *p; + struct vfsmount *h_mnt; + struct au_branch *br; + int err; + struct path path; + + br = au_sbr(sb, nsi_lock->bindex); + h_mnt = au_br_mnt(br); + h_sb = h_mnt->mnt_sb; + /* todo: call lower fh_to_dentry()? fh_to_parent()? */ + lockdep_off(); + h_parent = exportfs_decode_fh(h_mnt, (void *)(fh + Fh_tail), + fh_len - Fh_tail, fh[Fh_h_type], + h_acceptable, /*context*/NULL); + lockdep_on(); + dentry = h_parent; + if (unlikely(!h_parent || IS_ERR(h_parent))) { + AuWarn1("%s decode_fh failed, %ld\n", + au_sbtype(h_sb), PTR_ERR(h_parent)); + goto out; + } + dentry = NULL; + if (unlikely(au_test_anon(h_parent))) { + AuWarn1("%s decode_fh returned a disconnected dentry\n", + au_sbtype(h_sb)); + goto out_h_parent; + } + + dentry = ERR_PTR(-ENOMEM); + pathname = (void *)__get_free_page(GFP_NOFS); + if (unlikely(!pathname)) + goto out_h_parent; + + root = sb->s_root; + path.mnt = h_mnt; + di_read_lock_parent(root, !AuLock_IR); + path.dentry = au_h_dptr(root, nsi_lock->bindex); + di_read_unlock(root, !AuLock_IR); + p = au_build_path(h_parent, &path, pathname, PAGE_SIZE, sb); + dentry = (void *)p; + if (IS_ERR(p)) + goto out_pathname; + + si_read_unlock(sb); + err = vfsub_kern_path(p, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path); + dentry = ERR_PTR(err); + if (unlikely(err)) + goto out_relock; + + dentry = ERR_PTR(-ENOENT); + AuDebugOn(au_test_anon(path.dentry)); + if (unlikely(d_really_is_negative(path.dentry))) + goto out_path; + + if (ino != d_inode(path.dentry)->i_ino) + dentry = au_lkup_by_ino(&path, ino, /*nsi_lock*/NULL); + else + dentry = dget(path.dentry); + +out_path: + path_put(&path); +out_relock: + if (unlikely(si_nfsd_read_lock(sb, nsi_lock) < 0)) + if (!IS_ERR(dentry)) { + dput(dentry); + dentry = ERR_PTR(-ESTALE); + } +out_pathname: + free_page((unsigned long)pathname); +out_h_parent: + dput(h_parent); +out: + AuTraceErrPtr(dentry); + return dentry; +} + +/* ---------------------------------------------------------------------- */ + +static struct dentry * +aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, + int fh_type) +{ + struct dentry *dentry; + __u32 *fh = fid->raw; + struct au_branch *br; + ino_t ino, dir_ino; + struct au_nfsd_si_lock nsi_lock = { + .force_lock = 0 + }; + + dentry = ERR_PTR(-ESTALE); + /* it should never happen, but the file handle is unreliable */ + if (unlikely(fh_len < Fh_tail)) + goto out; + nsi_lock.sigen = fh[Fh_sigen]; + nsi_lock.br_id = fh[Fh_br_id]; + + /* branch id may be wrapped around */ + br = NULL; + if (unlikely(si_nfsd_read_lock(sb, &nsi_lock))) + goto out; + nsi_lock.force_lock = 1; + + /* is this inode still cached? */ + ino = decode_ino(fh + Fh_ino); + /* it should never happen */ + if (unlikely(ino == AUFS_ROOT_INO)) + goto out_unlock; + + dir_ino = decode_ino(fh + Fh_dir_ino); + dentry = decode_by_ino(sb, ino, dir_ino); + if (IS_ERR(dentry)) + goto out_unlock; + if (dentry) + goto accept; + + /* is the parent dir cached? */ + br = au_sbr(sb, nsi_lock.bindex); + au_lcnt_inc(&br->br_nfiles); + dentry = decode_by_dir_ino(sb, ino, dir_ino, &nsi_lock); + if (IS_ERR(dentry)) + goto out_unlock; + if (dentry) + goto accept; + + /* lookup path */ + dentry = decode_by_path(sb, ino, fh, fh_len, &nsi_lock); + if (IS_ERR(dentry)) + goto out_unlock; + if (unlikely(!dentry)) + /* todo?: make it ESTALE */ + goto out_unlock; + +accept: + if (!au_digen_test(dentry, au_sigen(sb)) + && d_inode(dentry)->i_generation == fh[Fh_igen]) + goto out_unlock; /* success */ + + dput(dentry); + dentry = ERR_PTR(-ESTALE); +out_unlock: + if (br) + au_lcnt_dec(&br->br_nfiles); + si_read_unlock(sb); +out: + AuTraceErrPtr(dentry); + return dentry; +} + +#if 0 /* reserved for future use */ +/* support subtreecheck option */ +static struct dentry *aufs_fh_to_parent(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) +{ + struct dentry *parent; + __u32 *fh = fid->raw; + ino_t dir_ino; + + dir_ino = decode_ino(fh + Fh_dir_ino); + parent = decode_by_ino(sb, dir_ino, 0); + if (IS_ERR(parent)) + goto out; + if (!parent) + parent = decode_by_path(sb, au_br_index(sb, fh[Fh_br_id]), + dir_ino, fh, fh_len); + +out: + AuTraceErrPtr(parent); + return parent; +} +#endif + +/* ---------------------------------------------------------------------- */ + +static int aufs_encode_fh(struct inode *inode, __u32 *fh, int *max_len, + struct inode *dir) +{ + int err; + aufs_bindex_t bindex; + struct super_block *sb, *h_sb; + struct dentry *dentry, *parent, *h_parent; + struct inode *h_dir; + struct au_branch *br; + + err = -ENOSPC; + if (unlikely(*max_len <= Fh_tail)) { + AuWarn1("NFSv2 client (max_len %d)?\n", *max_len); + goto out; + } + + err = FILEID_ROOT; + if (inode->i_ino == AUFS_ROOT_INO) { + AuDebugOn(inode->i_ino != AUFS_ROOT_INO); + goto out; + } + + h_parent = NULL; + sb = inode->i_sb; + err = si_read_lock(sb, AuLock_FLUSH); + if (unlikely(err)) + goto out; + +#ifdef CONFIG_AUFS_DEBUG + if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) + AuWarn1("NFS-exporting requires xino\n"); +#endif + err = -EIO; + parent = NULL; + ii_read_lock_child(inode); + bindex = au_ibtop(inode); + if (!dir) { + dentry = d_find_any_alias(inode); + if (unlikely(!dentry)) + goto out_unlock; + AuDebugOn(au_test_anon(dentry)); + parent = dget_parent(dentry); + dput(dentry); + if (unlikely(!parent)) + goto out_unlock; + if (d_really_is_positive(parent)) + dir = d_inode(parent); + } + + ii_read_lock_parent(dir); + h_dir = au_h_iptr(dir, bindex); + ii_read_unlock(dir); + if (unlikely(!h_dir)) + goto out_parent; + h_parent = d_find_any_alias(h_dir); + if (unlikely(!h_parent)) + goto out_hparent; + + err = -EPERM; + br = au_sbr(sb, bindex); + h_sb = au_br_sb(br); + if (unlikely(!h_sb->s_export_op)) { + AuErr1("%s branch is not exportable\n", au_sbtype(h_sb)); + goto out_hparent; + } + + fh[Fh_br_id] = br->br_id; + fh[Fh_sigen] = au_sigen(sb); + encode_ino(fh + Fh_ino, inode->i_ino); + encode_ino(fh + Fh_dir_ino, dir->i_ino); + fh[Fh_igen] = inode->i_generation; + + *max_len -= Fh_tail; + fh[Fh_h_type] = exportfs_encode_fh(h_parent, (void *)(fh + Fh_tail), + max_len, + /*connectable or subtreecheck*/0); + err = fh[Fh_h_type]; + *max_len += Fh_tail; + /* todo: macros? */ + if (err != FILEID_INVALID) + err = 99; + else + AuWarn1("%s encode_fh failed\n", au_sbtype(h_sb)); + +out_hparent: + dput(h_parent); +out_parent: + dput(parent); +out_unlock: + ii_read_unlock(inode); + si_read_unlock(sb); +out: + if (unlikely(err < 0)) + err = FILEID_INVALID; + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int aufs_commit_metadata(struct inode *inode) +{ + int err; + aufs_bindex_t bindex; + struct super_block *sb; + struct inode *h_inode; + int (*f)(struct inode *inode); + + sb = inode->i_sb; + si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); + ii_write_lock_child(inode); + bindex = au_ibtop(inode); + AuDebugOn(bindex < 0); + h_inode = au_h_iptr(inode, bindex); + + f = h_inode->i_sb->s_export_op->commit_metadata; + if (f) + err = f(h_inode); + else { + struct writeback_control wbc = { + .sync_mode = WB_SYNC_ALL, + .nr_to_write = 0 /* metadata only */ + }; + + err = sync_inode(h_inode, &wbc); + } + + au_cpup_attr_timesizes(inode); + ii_write_unlock(inode); + si_read_unlock(sb); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static struct export_operations aufs_export_op = { + .fh_to_dentry = aufs_fh_to_dentry, + /* .fh_to_parent = aufs_fh_to_parent, */ + .encode_fh = aufs_encode_fh, + .commit_metadata = aufs_commit_metadata +}; + +void au_export_init(struct super_block *sb) +{ + struct au_sbinfo *sbinfo; + __u32 u; + + BUILD_BUG_ON_MSG(IS_BUILTIN(CONFIG_AUFS_FS) + && IS_MODULE(CONFIG_EXPORTFS), + AUFS_NAME ": unsupported configuration " + "CONFIG_EXPORTFS=m and CONFIG_AUFS_FS=y"); + + sb->s_export_op = &aufs_export_op; + sbinfo = au_sbi(sb); + sbinfo->si_xigen = NULL; + get_random_bytes(&u, sizeof(u)); + BUILD_BUG_ON(sizeof(u) != sizeof(int)); + atomic_set(&sbinfo->si_xigen_next, u); +} diff --git a/fs/aufs/f_op.c b/fs/aufs/f_op.c new file mode 100644 index 0000000000000..cb02e256fa00a --- /dev/null +++ b/fs/aufs/f_op.c @@ -0,0 +1,775 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * file and vm operations + */ + +#include +#include +#include +#include +#include "aufs.h" + +int au_do_open_nondir(struct file *file, int flags, struct file *h_file) +{ + int err; + aufs_bindex_t bindex; + struct dentry *dentry, *h_dentry; + struct au_finfo *finfo; + struct inode *h_inode; + + FiMustWriteLock(file); + + err = 0; + dentry = file->f_path.dentry; + AuDebugOn(IS_ERR_OR_NULL(dentry)); + finfo = au_fi(file); + memset(&finfo->fi_htop, 0, sizeof(finfo->fi_htop)); + atomic_set(&finfo->fi_mmapped, 0); + bindex = au_dbtop(dentry); + if (!h_file) { + h_dentry = au_h_dptr(dentry, bindex); + err = vfsub_test_mntns(file->f_path.mnt, h_dentry->d_sb); + if (unlikely(err)) + goto out; + h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); + if (IS_ERR(h_file)) { + err = PTR_ERR(h_file); + goto out; + } + } else { + h_dentry = h_file->f_path.dentry; + err = vfsub_test_mntns(file->f_path.mnt, h_dentry->d_sb); + if (unlikely(err)) + goto out; + /* br ref is already inc-ed */ + } + + if ((flags & __O_TMPFILE) + && !(flags & O_EXCL)) { + h_inode = file_inode(h_file); + spin_lock(&h_inode->i_lock); + h_inode->i_state |= I_LINKABLE; + spin_unlock(&h_inode->i_lock); + } + au_set_fbtop(file, bindex); + au_set_h_fptr(file, bindex, h_file); + au_update_figen(file); + /* todo: necessary? */ + /* file->f_ra = h_file->f_ra; */ + +out: + return err; +} + +static int aufs_open_nondir(struct inode *inode __maybe_unused, + struct file *file) +{ + int err; + struct super_block *sb; + struct au_do_open_args args = { + .open = au_do_open_nondir + }; + + AuDbg("%pD, f_flags 0x%x, f_mode 0x%x\n", + file, vfsub_file_flags(file), file->f_mode); + + sb = file->f_path.dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH); + err = au_do_open(file, &args); + si_read_unlock(sb); + return err; +} + +int aufs_release_nondir(struct inode *inode __maybe_unused, struct file *file) +{ + struct au_finfo *finfo; + aufs_bindex_t bindex; + + finfo = au_fi(file); + au_hbl_del(&finfo->fi_hlist, + &au_sbi(file->f_path.dentry->d_sb)->si_files); + bindex = finfo->fi_btop; + if (bindex >= 0) + au_set_h_fptr(file, bindex, NULL); + + au_finfo_fin(file); + return 0; +} + +/* ---------------------------------------------------------------------- */ + +static int au_do_flush_nondir(struct file *file, fl_owner_t id) +{ + int err; + struct file *h_file; + + err = 0; + h_file = au_hf_top(file); + if (h_file) + err = vfsub_flush(h_file, id); + return err; +} + +static int aufs_flush_nondir(struct file *file, fl_owner_t id) +{ + return au_do_flush(file, id, au_do_flush_nondir); +} + +/* ---------------------------------------------------------------------- */ +/* + * read and write functions acquire [fdi]_rwsem once, but release before + * mmap_sem. This is because to stop a race condition between mmap(2). + * Releasing these aufs-rwsem should be safe, no branch-management (by keeping + * si_rwsem), no harmful copy-up should happen. Actually copy-up may happen in + * read functions after [fdi]_rwsem are released, but it should be harmless. + */ + +/* Callers should call au_read_post() or fput() in the end */ +struct file *au_read_pre(struct file *file, int keep_fi, unsigned int lsc) +{ + struct file *h_file; + int err; + + err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0, lsc); + if (!err) { + di_read_unlock(file->f_path.dentry, AuLock_IR); + h_file = au_hf_top(file); + get_file(h_file); + if (!keep_fi) + fi_read_unlock(file); + } else + h_file = ERR_PTR(err); + + return h_file; +} + +static void au_read_post(struct inode *inode, struct file *h_file) +{ + /* update without lock, I don't think it a problem */ + fsstack_copy_attr_atime(inode, file_inode(h_file)); + fput(h_file); +} + +struct au_write_pre { + /* input */ + unsigned int lsc; + + /* output */ + blkcnt_t blks; + aufs_bindex_t btop; +}; + +/* + * return with iinfo is write-locked + * callers should call au_write_post() or iinfo_write_unlock() + fput() in the + * end + */ +static struct file *au_write_pre(struct file *file, int do_ready, + struct au_write_pre *wpre) +{ + struct file *h_file; + struct dentry *dentry; + int err; + unsigned int lsc; + struct au_pin pin; + + lsc = 0; + if (wpre) + lsc = wpre->lsc; + err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1, lsc); + h_file = ERR_PTR(err); + if (unlikely(err)) + goto out; + + dentry = file->f_path.dentry; + if (do_ready) { + err = au_ready_to_write(file, -1, &pin); + if (unlikely(err)) { + h_file = ERR_PTR(err); + di_write_unlock(dentry); + goto out_fi; + } + } + + di_downgrade_lock(dentry, /*flags*/0); + if (wpre) + wpre->btop = au_fbtop(file); + h_file = au_hf_top(file); + get_file(h_file); + if (wpre) + wpre->blks = file_inode(h_file)->i_blocks; + if (do_ready) + au_unpin(&pin); + di_read_unlock(dentry, /*flags*/0); + +out_fi: + fi_write_unlock(file); +out: + return h_file; +} + +static void au_write_post(struct inode *inode, struct file *h_file, + struct au_write_pre *wpre, ssize_t written) +{ + struct inode *h_inode; + + au_cpup_attr_timesizes(inode); + AuDebugOn(au_ibtop(inode) != wpre->btop); + h_inode = file_inode(h_file); + inode->i_mode = h_inode->i_mode; + ii_write_unlock(inode); + /* AuDbg("blks %llu, %llu\n", (u64)blks, (u64)h_inode->i_blocks); */ + if (written > 0) + au_fhsm_wrote(inode->i_sb, wpre->btop, + /*force*/h_inode->i_blocks > wpre->blks); + fput(h_file); +} + +/* + * todo: very ugly + * it locks both of i_mutex and si_rwsem for read in safe. + * if the plink maintenance mode continues forever (that is the problem), + * may loop forever. + */ +static void au_mtx_and_read_lock(struct inode *inode) +{ + int err; + struct super_block *sb = inode->i_sb; + + while (1) { + inode_lock(inode); + err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); + if (!err) + break; + inode_unlock(inode); + si_read_lock(sb, AuLock_NOPLMW); + si_read_unlock(sb); + } +} + +static ssize_t au_do_iter(struct file *h_file, int rw, struct kiocb *kio, + struct iov_iter *iov_iter) +{ + ssize_t err; + struct file *file; + ssize_t (*iter)(struct kiocb *, struct iov_iter *); + + err = security_file_permission(h_file, rw); + if (unlikely(err)) + goto out; + + err = -ENOSYS; /* the branch doesn't have its ->(read|write)_iter() */ + iter = NULL; + if (rw == MAY_READ) + iter = h_file->f_op->read_iter; + else if (rw == MAY_WRITE) + iter = h_file->f_op->write_iter; + + file = kio->ki_filp; + kio->ki_filp = h_file; + if (iter) { + lockdep_off(); + err = iter(kio, iov_iter); + lockdep_on(); + } else + /* currently there is no such fs */ + WARN_ON_ONCE(1); + kio->ki_filp = file; + +out: + return err; +} + +static ssize_t aufs_read_iter(struct kiocb *kio, struct iov_iter *iov_iter) +{ + ssize_t err; + struct file *file, *h_file; + struct inode *inode; + struct super_block *sb; + + file = kio->ki_filp; + inode = file_inode(file); + sb = inode->i_sb; + si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); + + h_file = au_read_pre(file, /*keep_fi*/1, /*lsc*/0); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; + + if (au_test_loopback_kthread()) { + au_warn_loopback(h_file->f_path.dentry->d_sb); + if (file->f_mapping != h_file->f_mapping) { + file->f_mapping = h_file->f_mapping; + smp_mb(); /* unnecessary? */ + } + } + fi_read_unlock(file); + + err = au_do_iter(h_file, MAY_READ, kio, iov_iter); + /* todo: necessary? */ + /* file->f_ra = h_file->f_ra; */ + au_read_post(inode, h_file); + +out: + si_read_unlock(sb); + return err; +} + +static ssize_t aufs_write_iter(struct kiocb *kio, struct iov_iter *iov_iter) +{ + ssize_t err; + struct au_write_pre wpre; + struct inode *inode; + struct file *file, *h_file; + + file = kio->ki_filp; + inode = file_inode(file); + au_mtx_and_read_lock(inode); + + wpre.lsc = 0; + h_file = au_write_pre(file, /*do_ready*/1, &wpre); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; + + err = au_do_iter(h_file, MAY_WRITE, kio, iov_iter); + au_write_post(inode, h_file, &wpre, err); + +out: + si_read_unlock(inode->i_sb); + inode_unlock(inode); + return err; +} + +/* + * We may be able to remove aufs_splice_{read,write}() since almost all FSes + * don't have their own .splice_{read,write} implimentations, and they use + * generic_file_splice_read() and iter_file_splice_write() who can act like the + * simple converters to f_op->iter_read() and ->iter_write(). + * But we keep our own implementations because some non-mainlined FSes may have + * their own .splice_{read,write} implimentations and aufs doesn't want to take + * away an opportunity to co-work with aufs from them. + */ +static ssize_t aufs_splice_read(struct file *file, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags) +{ + ssize_t err; + struct file *h_file; + struct inode *inode; + struct super_block *sb; + + inode = file_inode(file); + sb = inode->i_sb; + si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); + + h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; + + err = vfsub_splice_to(h_file, ppos, pipe, len, flags); + /* todo: necessary? */ + /* file->f_ra = h_file->f_ra; */ + au_read_post(inode, h_file); + +out: + si_read_unlock(sb); + return err; +} + +static ssize_t +aufs_splice_write(struct pipe_inode_info *pipe, struct file *file, loff_t *ppos, + size_t len, unsigned int flags) +{ + ssize_t err; + struct au_write_pre wpre; + struct inode *inode; + struct file *h_file; + + inode = file_inode(file); + au_mtx_and_read_lock(inode); + + wpre.lsc = 0; + h_file = au_write_pre(file, /*do_ready*/1, &wpre); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; + + err = vfsub_splice_from(pipe, h_file, ppos, len, flags); + au_write_post(inode, h_file, &wpre, err); + +out: + si_read_unlock(inode->i_sb); + inode_unlock(inode); + return err; +} + +static long aufs_fallocate(struct file *file, int mode, loff_t offset, + loff_t len) +{ + long err; + struct au_write_pre wpre; + struct inode *inode; + struct file *h_file; + + inode = file_inode(file); + au_mtx_and_read_lock(inode); + + wpre.lsc = 0; + h_file = au_write_pre(file, /*do_ready*/1, &wpre); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; + + lockdep_off(); + err = vfs_fallocate(h_file, mode, offset, len); + lockdep_on(); + /* + * we don't need to call file_modifed() here since au_write_post() + * is equivalent and copies-up all timestamps and permission bits. + */ + au_write_post(inode, h_file, &wpre, /*written*/1); + +out: + si_read_unlock(inode->i_sb); + inode_unlock(inode); + return err; +} + +static ssize_t aufs_copy_file_range(struct file *src, loff_t src_pos, + struct file *dst, loff_t dst_pos, + size_t len, unsigned int flags) +{ + ssize_t err; + struct au_write_pre wpre; + enum { SRC, DST }; + struct { + struct inode *inode; + struct file *h_file; + struct super_block *h_sb; + } a[2]; +#define a_src a[SRC] +#define a_dst a[DST] + + err = -EINVAL; + a_src.inode = file_inode(src); + if (unlikely(!S_ISREG(a_src.inode->i_mode))) + goto out; + a_dst.inode = file_inode(dst); + if (unlikely(!S_ISREG(a_dst.inode->i_mode))) + goto out; + + au_mtx_and_read_lock(a_dst.inode); + /* + * in order to match the order in di_write_lock2_{child,parent}(), + * use f_path.dentry for this comparison. + */ + if (src->f_path.dentry < dst->f_path.dentry) { + a_src.h_file = au_read_pre(src, /*keep_fi*/1, AuLsc_FI_1); + err = PTR_ERR(a_src.h_file); + if (IS_ERR(a_src.h_file)) + goto out_si; + + wpre.lsc = AuLsc_FI_2; + a_dst.h_file = au_write_pre(dst, /*do_ready*/1, &wpre); + err = PTR_ERR(a_dst.h_file); + if (IS_ERR(a_dst.h_file)) { + au_read_post(a_src.inode, a_src.h_file); + goto out_si; + } + } else { + wpre.lsc = AuLsc_FI_1; + a_dst.h_file = au_write_pre(dst, /*do_ready*/1, &wpre); + err = PTR_ERR(a_dst.h_file); + if (IS_ERR(a_dst.h_file)) + goto out_si; + + a_src.h_file = au_read_pre(src, /*keep_fi*/1, AuLsc_FI_2); + err = PTR_ERR(a_src.h_file); + if (IS_ERR(a_src.h_file)) { + au_write_post(a_dst.inode, a_dst.h_file, &wpre, + /*written*/0); + goto out_si; + } + } + + err = -EXDEV; + a_src.h_sb = file_inode(a_src.h_file)->i_sb; + a_dst.h_sb = file_inode(a_dst.h_file)->i_sb; + if (unlikely(a_src.h_sb != a_dst.h_sb)) { + AuDbgFile(src); + AuDbgFile(dst); + goto out_file; + } + + err = vfsub_copy_file_range(a_src.h_file, src_pos, a_dst.h_file, + dst_pos, len, flags); + +out_file: + au_write_post(a_dst.inode, a_dst.h_file, &wpre, err); + fi_read_unlock(src); + au_read_post(a_src.inode, a_src.h_file); +out_si: + si_read_unlock(a_dst.inode->i_sb); + inode_unlock(a_dst.inode); +out: + return err; +#undef a_src +#undef a_dst +} + +/* ---------------------------------------------------------------------- */ + +/* + * The locking order around current->mmap_sem. + * - in most and regular cases + * file I/O syscall -- aufs_read() or something + * -- si_rwsem for read -- mmap_sem + * (Note that [fdi]i_rwsem are released before mmap_sem). + * - in mmap case + * mmap(2) -- mmap_sem -- aufs_mmap() -- si_rwsem for read -- [fdi]i_rwsem + * This AB-BA order is definitely bad, but is not a problem since "si_rwsem for + * read" allows multiple processes to acquire it and [fdi]i_rwsem are not held + * in file I/O. Aufs needs to stop lockdep in aufs_mmap() though. + * It means that when aufs acquires si_rwsem for write, the process should never + * acquire mmap_sem. + * + * Actually aufs_iterate() holds [fdi]i_rwsem before mmap_sem, but this is not a + * problem either since any directory is not able to be mmap-ed. + * The similar scenario is applied to aufs_readlink() too. + */ + +#if 0 /* stop calling security_file_mmap() */ +/* cf. linux/include/linux/mman.h: calc_vm_prot_bits() */ +#define AuConv_VM_PROT(f, b) _calc_vm_trans(f, VM_##b, PROT_##b) + +static unsigned long au_arch_prot_conv(unsigned long flags) +{ + /* currently ppc64 only */ +#ifdef CONFIG_PPC64 + /* cf. linux/arch/powerpc/include/asm/mman.h */ + AuDebugOn(arch_calc_vm_prot_bits(-1) != VM_SAO); + return AuConv_VM_PROT(flags, SAO); +#else + AuDebugOn(arch_calc_vm_prot_bits(-1)); + return 0; +#endif +} + +static unsigned long au_prot_conv(unsigned long flags) +{ + return AuConv_VM_PROT(flags, READ) + | AuConv_VM_PROT(flags, WRITE) + | AuConv_VM_PROT(flags, EXEC) + | au_arch_prot_conv(flags); +} + +/* cf. linux/include/linux/mman.h: calc_vm_flag_bits() */ +#define AuConv_VM_MAP(f, b) _calc_vm_trans(f, VM_##b, MAP_##b) + +static unsigned long au_flag_conv(unsigned long flags) +{ + return AuConv_VM_MAP(flags, GROWSDOWN) + | AuConv_VM_MAP(flags, DENYWRITE) + | AuConv_VM_MAP(flags, LOCKED); +} +#endif + +static int aufs_mmap(struct file *file, struct vm_area_struct *vma) +{ + int err; + const unsigned char wlock + = (file->f_mode & FMODE_WRITE) && (vma->vm_flags & VM_SHARED); + struct super_block *sb; + struct file *h_file; + struct inode *inode; + + AuDbgVmRegion(file, vma); + + inode = file_inode(file); + sb = inode->i_sb; + lockdep_off(); + si_read_lock(sb, AuLock_NOPLMW); + + h_file = au_write_pre(file, wlock, /*wpre*/NULL); + lockdep_on(); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; + + err = 0; + au_set_mmapped(file); + au_vm_file_reset(vma, h_file); + /* + * we cannot call security_mmap_file() here since it may acquire + * mmap_sem or i_mutex. + * + * err = security_mmap_file(h_file, au_prot_conv(vma->vm_flags), + * au_flag_conv(vma->vm_flags)); + */ + if (!err) + err = call_mmap(h_file, vma); + if (!err) { + au_vm_prfile_set(vma, file); + fsstack_copy_attr_atime(inode, file_inode(h_file)); + goto out_fput; /* success */ + } + au_unset_mmapped(file); + au_vm_file_reset(vma, file); + +out_fput: + lockdep_off(); + ii_write_unlock(inode); + lockdep_on(); + fput(h_file); +out: + lockdep_off(); + si_read_unlock(sb); + lockdep_on(); + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int aufs_fsync_nondir(struct file *file, loff_t start, loff_t end, + int datasync) +{ + int err; + struct au_write_pre wpre; + struct inode *inode; + struct file *h_file; + + err = 0; /* -EBADF; */ /* posix? */ + if (unlikely(!(file->f_mode & FMODE_WRITE))) + goto out; + + inode = file_inode(file); + au_mtx_and_read_lock(inode); + + wpre.lsc = 0; + h_file = au_write_pre(file, /*do_ready*/1, &wpre); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out_unlock; + + err = vfsub_fsync(h_file, &h_file->f_path, datasync); + au_write_post(inode, h_file, &wpre, /*written*/0); + +out_unlock: + si_read_unlock(inode->i_sb); + inode_unlock(inode); +out: + return err; +} + +static int aufs_fasync(int fd, struct file *file, int flag) +{ + int err; + struct file *h_file; + struct super_block *sb; + + sb = file->f_path.dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); + + h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; + + if (h_file->f_op->fasync) + err = h_file->f_op->fasync(fd, h_file, flag); + fput(h_file); /* instead of au_read_post() */ + +out: + si_read_unlock(sb); + return err; +} + +static int aufs_setfl(struct file *file, unsigned long arg) +{ + int err; + struct file *h_file; + struct super_block *sb; + + sb = file->f_path.dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); + + h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; + + /* stop calling h_file->fasync */ + arg |= vfsub_file_flags(file) & FASYNC; + err = setfl(/*unused fd*/-1, h_file, arg); + fput(h_file); /* instead of au_read_post() */ + +out: + si_read_unlock(sb); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* no one supports this operation, currently */ +#if 0 /* reserved for future use */ +static ssize_t aufs_sendpage(struct file *file, struct page *page, int offset, + size_t len, loff_t *pos, int more) +{ +} +#endif + +/* ---------------------------------------------------------------------- */ + +const struct file_operations aufs_file_fop = { + .owner = THIS_MODULE, + + .llseek = default_llseek, + + .read_iter = aufs_read_iter, + .write_iter = aufs_write_iter, + +#ifdef CONFIG_AUFS_POLL + .poll = aufs_poll, +#endif + .unlocked_ioctl = aufs_ioctl_nondir, +#ifdef CONFIG_COMPAT + .compat_ioctl = aufs_compat_ioctl_nondir, +#endif + .mmap = aufs_mmap, + .open = aufs_open_nondir, + .flush = aufs_flush_nondir, + .release = aufs_release_nondir, + .fsync = aufs_fsync_nondir, + .fasync = aufs_fasync, + /* .sendpage = aufs_sendpage, */ + .setfl = aufs_setfl, + .splice_write = aufs_splice_write, + .splice_read = aufs_splice_read, +#if 0 /* reserved for future use */ + .aio_splice_write = aufs_aio_splice_write, + .aio_splice_read = aufs_aio_splice_read, +#endif + .fallocate = aufs_fallocate, + .copy_file_range = aufs_copy_file_range +}; diff --git a/fs/aufs/fhsm.c b/fs/aufs/fhsm.c new file mode 100644 index 0000000000000..9f3f8ba3fa765 --- /dev/null +++ b/fs/aufs/fhsm.c @@ -0,0 +1,427 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2011-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * File-based Hierarchy Storage Management + */ + +#include +#include +#include +#include +#include "aufs.h" + +static aufs_bindex_t au_fhsm_bottom(struct super_block *sb) +{ + struct au_sbinfo *sbinfo; + struct au_fhsm *fhsm; + + SiMustAnyLock(sb); + + sbinfo = au_sbi(sb); + fhsm = &sbinfo->si_fhsm; + AuDebugOn(!fhsm); + return fhsm->fhsm_bottom; +} + +void au_fhsm_set_bottom(struct super_block *sb, aufs_bindex_t bindex) +{ + struct au_sbinfo *sbinfo; + struct au_fhsm *fhsm; + + SiMustWriteLock(sb); + + sbinfo = au_sbi(sb); + fhsm = &sbinfo->si_fhsm; + AuDebugOn(!fhsm); + fhsm->fhsm_bottom = bindex; +} + +/* ---------------------------------------------------------------------- */ + +static int au_fhsm_test_jiffy(struct au_sbinfo *sbinfo, struct au_branch *br) +{ + struct au_br_fhsm *bf; + + bf = br->br_fhsm; + MtxMustLock(&bf->bf_lock); + + return !bf->bf_readable + || time_after(jiffies, + bf->bf_jiffy + sbinfo->si_fhsm.fhsm_expire); +} + +/* ---------------------------------------------------------------------- */ + +static void au_fhsm_notify(struct super_block *sb, int val) +{ + struct au_sbinfo *sbinfo; + struct au_fhsm *fhsm; + + SiMustAnyLock(sb); + + sbinfo = au_sbi(sb); + fhsm = &sbinfo->si_fhsm; + if (au_fhsm_pid(fhsm) + && atomic_read(&fhsm->fhsm_readable) != -1) { + atomic_set(&fhsm->fhsm_readable, val); + if (val) + wake_up(&fhsm->fhsm_wqh); + } +} + +static int au_fhsm_stfs(struct super_block *sb, aufs_bindex_t bindex, + struct aufs_stfs *rstfs, int do_lock, int do_notify) +{ + int err; + struct au_branch *br; + struct au_br_fhsm *bf; + + br = au_sbr(sb, bindex); + AuDebugOn(au_br_rdonly(br)); + bf = br->br_fhsm; + AuDebugOn(!bf); + + if (do_lock) + mutex_lock(&bf->bf_lock); + else + MtxMustLock(&bf->bf_lock); + + /* sb->s_root for NFS is unreliable */ + err = au_br_stfs(br, &bf->bf_stfs); + if (unlikely(err)) { + AuErr1("FHSM failed (%d), b%d, ignored.\n", bindex, err); + goto out; + } + + bf->bf_jiffy = jiffies; + bf->bf_readable = 1; + if (do_notify) + au_fhsm_notify(sb, /*val*/1); + if (rstfs) + *rstfs = bf->bf_stfs; + +out: + if (do_lock) + mutex_unlock(&bf->bf_lock); + au_fhsm_notify(sb, /*val*/1); + + return err; +} + +void au_fhsm_wrote(struct super_block *sb, aufs_bindex_t bindex, int force) +{ + int err; + struct au_sbinfo *sbinfo; + struct au_fhsm *fhsm; + struct au_branch *br; + struct au_br_fhsm *bf; + + AuDbg("b%d, force %d\n", bindex, force); + SiMustAnyLock(sb); + + sbinfo = au_sbi(sb); + fhsm = &sbinfo->si_fhsm; + if (!au_ftest_si(sbinfo, FHSM) + || fhsm->fhsm_bottom == bindex) + return; + + br = au_sbr(sb, bindex); + bf = br->br_fhsm; + AuDebugOn(!bf); + mutex_lock(&bf->bf_lock); + if (force + || au_fhsm_pid(fhsm) + || au_fhsm_test_jiffy(sbinfo, br)) + err = au_fhsm_stfs(sb, bindex, /*rstfs*/NULL, /*do_lock*/0, + /*do_notify*/1); + mutex_unlock(&bf->bf_lock); +} + +void au_fhsm_wrote_all(struct super_block *sb, int force) +{ + aufs_bindex_t bindex, bbot; + struct au_branch *br; + + /* exclude the bottom */ + bbot = au_fhsm_bottom(sb); + for (bindex = 0; bindex < bbot; bindex++) { + br = au_sbr(sb, bindex); + if (au_br_fhsm(br->br_perm)) + au_fhsm_wrote(sb, bindex, force); + } +} + +/* ---------------------------------------------------------------------- */ + +static __poll_t au_fhsm_poll(struct file *file, struct poll_table_struct *wait) +{ + __poll_t mask; + struct au_sbinfo *sbinfo; + struct au_fhsm *fhsm; + + mask = 0; + sbinfo = file->private_data; + fhsm = &sbinfo->si_fhsm; + poll_wait(file, &fhsm->fhsm_wqh, wait); + if (atomic_read(&fhsm->fhsm_readable)) + mask = EPOLLIN /* | EPOLLRDNORM */; + + if (!mask) + AuDbg("mask 0x%x\n", mask); + return mask; +} + +static int au_fhsm_do_read_one(struct aufs_stbr __user *stbr, + struct aufs_stfs *stfs, __s16 brid) +{ + int err; + + err = copy_to_user(&stbr->stfs, stfs, sizeof(*stfs)); + if (!err) + err = __put_user(brid, &stbr->brid); + if (unlikely(err)) + err = -EFAULT; + + return err; +} + +static ssize_t au_fhsm_do_read(struct super_block *sb, + struct aufs_stbr __user *stbr, size_t count) +{ + ssize_t err; + int nstbr; + aufs_bindex_t bindex, bbot; + struct au_branch *br; + struct au_br_fhsm *bf; + + /* except the bottom branch */ + err = 0; + nstbr = 0; + bbot = au_fhsm_bottom(sb); + for (bindex = 0; !err && bindex < bbot; bindex++) { + br = au_sbr(sb, bindex); + if (!au_br_fhsm(br->br_perm)) + continue; + + bf = br->br_fhsm; + mutex_lock(&bf->bf_lock); + if (bf->bf_readable) { + err = -EFAULT; + if (count >= sizeof(*stbr)) + err = au_fhsm_do_read_one(stbr++, &bf->bf_stfs, + br->br_id); + if (!err) { + bf->bf_readable = 0; + count -= sizeof(*stbr); + nstbr++; + } + } + mutex_unlock(&bf->bf_lock); + } + if (!err) + err = sizeof(*stbr) * nstbr; + + return err; +} + +static ssize_t au_fhsm_read(struct file *file, char __user *buf, size_t count, + loff_t *pos) +{ + ssize_t err; + int readable; + aufs_bindex_t nfhsm, bindex, bbot; + struct au_sbinfo *sbinfo; + struct au_fhsm *fhsm; + struct au_branch *br; + struct super_block *sb; + + err = 0; + sbinfo = file->private_data; + fhsm = &sbinfo->si_fhsm; +need_data: + spin_lock_irq(&fhsm->fhsm_wqh.lock); + if (!atomic_read(&fhsm->fhsm_readable)) { + if (vfsub_file_flags(file) & O_NONBLOCK) + err = -EAGAIN; + else + err = wait_event_interruptible_locked_irq + (fhsm->fhsm_wqh, + atomic_read(&fhsm->fhsm_readable)); + } + spin_unlock_irq(&fhsm->fhsm_wqh.lock); + if (unlikely(err)) + goto out; + + /* sb may already be dead */ + au_rw_read_lock(&sbinfo->si_rwsem); + readable = atomic_read(&fhsm->fhsm_readable); + if (readable > 0) { + sb = sbinfo->si_sb; + AuDebugOn(!sb); + /* exclude the bottom branch */ + nfhsm = 0; + bbot = au_fhsm_bottom(sb); + for (bindex = 0; bindex < bbot; bindex++) { + br = au_sbr(sb, bindex); + if (au_br_fhsm(br->br_perm)) + nfhsm++; + } + err = -EMSGSIZE; + if (nfhsm * sizeof(struct aufs_stbr) <= count) { + atomic_set(&fhsm->fhsm_readable, 0); + err = au_fhsm_do_read(sbinfo->si_sb, (void __user *)buf, + count); + } + } + au_rw_read_unlock(&sbinfo->si_rwsem); + if (!readable) + goto need_data; + +out: + return err; +} + +static int au_fhsm_release(struct inode *inode, struct file *file) +{ + struct au_sbinfo *sbinfo; + struct au_fhsm *fhsm; + + /* sb may already be dead */ + sbinfo = file->private_data; + fhsm = &sbinfo->si_fhsm; + spin_lock(&fhsm->fhsm_spin); + fhsm->fhsm_pid = 0; + spin_unlock(&fhsm->fhsm_spin); + kobject_put(&sbinfo->si_kobj); + + return 0; +} + +static const struct file_operations au_fhsm_fops = { + .owner = THIS_MODULE, + .llseek = noop_llseek, + .read = au_fhsm_read, + .poll = au_fhsm_poll, + .release = au_fhsm_release +}; + +int au_fhsm_fd(struct super_block *sb, int oflags) +{ + int err, fd; + struct au_sbinfo *sbinfo; + struct au_fhsm *fhsm; + + err = -EPERM; + if (unlikely(!capable(CAP_SYS_ADMIN))) + goto out; + + err = -EINVAL; + if (unlikely(oflags & ~(O_CLOEXEC | O_NONBLOCK))) + goto out; + + err = 0; + sbinfo = au_sbi(sb); + fhsm = &sbinfo->si_fhsm; + spin_lock(&fhsm->fhsm_spin); + if (!fhsm->fhsm_pid) + fhsm->fhsm_pid = current->pid; + else + err = -EBUSY; + spin_unlock(&fhsm->fhsm_spin); + if (unlikely(err)) + goto out; + + oflags |= O_RDONLY; + /* oflags |= FMODE_NONOTIFY; */ + fd = anon_inode_getfd("[aufs_fhsm]", &au_fhsm_fops, sbinfo, oflags); + err = fd; + if (unlikely(fd < 0)) + goto out_pid; + + /* succeed regardless 'fhsm' status */ + kobject_get(&sbinfo->si_kobj); + si_noflush_read_lock(sb); + if (au_ftest_si(sbinfo, FHSM)) + au_fhsm_wrote_all(sb, /*force*/0); + si_read_unlock(sb); + goto out; /* success */ + +out_pid: + spin_lock(&fhsm->fhsm_spin); + fhsm->fhsm_pid = 0; + spin_unlock(&fhsm->fhsm_spin); +out: + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +int au_fhsm_br_alloc(struct au_branch *br) +{ + int err; + + err = 0; + br->br_fhsm = kmalloc(sizeof(*br->br_fhsm), GFP_NOFS); + if (br->br_fhsm) + au_br_fhsm_init(br->br_fhsm); + else + err = -ENOMEM; + + return err; +} + +/* ---------------------------------------------------------------------- */ + +void au_fhsm_fin(struct super_block *sb) +{ + au_fhsm_notify(sb, /*val*/-1); +} + +void au_fhsm_init(struct au_sbinfo *sbinfo) +{ + struct au_fhsm *fhsm; + + fhsm = &sbinfo->si_fhsm; + spin_lock_init(&fhsm->fhsm_spin); + init_waitqueue_head(&fhsm->fhsm_wqh); + atomic_set(&fhsm->fhsm_readable, 0); + fhsm->fhsm_expire + = msecs_to_jiffies(AUFS_FHSM_CACHE_DEF_SEC * MSEC_PER_SEC); + fhsm->fhsm_bottom = -1; +} + +void au_fhsm_set(struct au_sbinfo *sbinfo, unsigned int sec) +{ + sbinfo->si_fhsm.fhsm_expire + = msecs_to_jiffies(sec * MSEC_PER_SEC); +} + +void au_fhsm_show(struct seq_file *seq, struct au_sbinfo *sbinfo) +{ + unsigned int u; + + if (!au_ftest_si(sbinfo, FHSM)) + return; + + u = jiffies_to_msecs(sbinfo->si_fhsm.fhsm_expire) / MSEC_PER_SEC; + if (u != AUFS_FHSM_CACHE_DEF_SEC) + seq_printf(seq, ",fhsm_sec=%u", u); +} diff --git a/fs/aufs/file.c b/fs/aufs/file.c new file mode 100644 index 0000000000000..df012d8b923cf --- /dev/null +++ b/fs/aufs/file.c @@ -0,0 +1,863 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * handling file/dir, and address_space operation + */ + +#ifdef CONFIG_AUFS_DEBUG +#include +#endif +#include +#include "aufs.h" + +/* drop flags for writing */ +unsigned int au_file_roflags(unsigned int flags) +{ + flags &= ~(O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_TRUNC); + flags |= O_RDONLY | O_NOATIME; + return flags; +} + +/* common functions to regular file and dir */ +struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags, + struct file *file, int force_wr) +{ + struct file *h_file; + struct dentry *h_dentry; + struct inode *h_inode; + struct super_block *sb; + struct au_branch *br; + struct path h_path; + int err; + + /* a race condition can happen between open and unlink/rmdir */ + h_file = ERR_PTR(-ENOENT); + h_dentry = au_h_dptr(dentry, bindex); + if (au_test_nfsd() && (!h_dentry || d_is_negative(h_dentry))) + goto out; + h_inode = d_inode(h_dentry); + spin_lock(&h_dentry->d_lock); + err = (!d_unhashed(dentry) && d_unlinked(h_dentry)) + /* || !d_inode(dentry)->i_nlink */ + ; + spin_unlock(&h_dentry->d_lock); + if (unlikely(err)) + goto out; + + sb = dentry->d_sb; + br = au_sbr(sb, bindex); + err = au_br_test_oflag(flags, br); + h_file = ERR_PTR(err); + if (unlikely(err)) + goto out; + + /* drop flags for writing */ + if (au_test_ro(sb, bindex, d_inode(dentry))) { + if (force_wr && !(flags & O_WRONLY)) + force_wr = 0; + flags = au_file_roflags(flags); + if (force_wr) { + h_file = ERR_PTR(-EROFS); + flags = au_file_roflags(flags); + if (unlikely(vfsub_native_ro(h_inode) + || IS_APPEND(h_inode))) + goto out; + flags &= ~O_ACCMODE; + flags |= O_WRONLY; + } + } + flags &= ~O_CREAT; + au_lcnt_inc(&br->br_nfiles); + h_path.dentry = h_dentry; + h_path.mnt = au_br_mnt(br); + h_file = vfsub_dentry_open(&h_path, flags); + if (IS_ERR(h_file)) + goto out_br; + + if (flags & __FMODE_EXEC) { + err = deny_write_access(h_file); + if (unlikely(err)) { + fput(h_file); + h_file = ERR_PTR(err); + goto out_br; + } + } + fsnotify_open(h_file); + goto out; /* success */ + +out_br: + au_lcnt_dec(&br->br_nfiles); +out: + return h_file; +} + +static int au_cmoo(struct dentry *dentry) +{ + int err, cmoo, matched; + unsigned int udba; + struct path h_path; + struct au_pin pin; + struct au_cp_generic cpg = { + .dentry = dentry, + .bdst = -1, + .bsrc = -1, + .len = -1, + .pin = &pin, + .flags = AuCpup_DTIME | AuCpup_HOPEN + }; + struct inode *delegated; + struct super_block *sb; + struct au_sbinfo *sbinfo; + struct au_fhsm *fhsm; + pid_t pid; + struct au_branch *br; + struct dentry *parent; + struct au_hinode *hdir; + + DiMustWriteLock(dentry); + IiMustWriteLock(d_inode(dentry)); + + err = 0; + if (IS_ROOT(dentry)) + goto out; + cpg.bsrc = au_dbtop(dentry); + if (!cpg.bsrc) + goto out; + + sb = dentry->d_sb; + sbinfo = au_sbi(sb); + fhsm = &sbinfo->si_fhsm; + pid = au_fhsm_pid(fhsm); + rcu_read_lock(); + matched = (pid + && (current->pid == pid + || rcu_dereference(current->real_parent)->pid == pid)); + rcu_read_unlock(); + if (matched) + goto out; + + br = au_sbr(sb, cpg.bsrc); + cmoo = au_br_cmoo(br->br_perm); + if (!cmoo) + goto out; + if (!d_is_reg(dentry)) + cmoo &= AuBrAttr_COO_ALL; + if (!cmoo) + goto out; + + parent = dget_parent(dentry); + di_write_lock_parent(parent); + err = au_wbr_do_copyup_bu(dentry, cpg.bsrc - 1); + cpg.bdst = err; + if (unlikely(err < 0)) { + err = 0; /* there is no upper writable branch */ + goto out_dgrade; + } + AuDbg("bsrc %d, bdst %d\n", cpg.bsrc, cpg.bdst); + + /* do not respect the coo attrib for the target branch */ + err = au_cpup_dirs(dentry, cpg.bdst); + if (unlikely(err)) + goto out_dgrade; + + di_downgrade_lock(parent, AuLock_IR); + udba = au_opt_udba(sb); + err = au_pin(&pin, dentry, cpg.bdst, udba, + AuPin_DI_LOCKED | AuPin_MNT_WRITE); + if (unlikely(err)) + goto out_parent; + + err = au_sio_cpup_simple(&cpg); + au_unpin(&pin); + if (unlikely(err)) + goto out_parent; + if (!(cmoo & AuBrWAttr_MOO)) + goto out_parent; /* success */ + + err = au_pin(&pin, dentry, cpg.bsrc, udba, + AuPin_DI_LOCKED | AuPin_MNT_WRITE); + if (unlikely(err)) + goto out_parent; + + h_path.mnt = au_br_mnt(br); + h_path.dentry = au_h_dptr(dentry, cpg.bsrc); + hdir = au_hi(d_inode(parent), cpg.bsrc); + delegated = NULL; + err = vfsub_unlink(hdir->hi_inode, &h_path, &delegated, /*force*/1); + au_unpin(&pin); + /* todo: keep h_dentry or not? */ + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal unlink\n"); + iput(delegated); + } + if (unlikely(err)) { + pr_err("unlink %pd after coo failed (%d), ignored\n", + dentry, err); + err = 0; + } + goto out_parent; /* success */ + +out_dgrade: + di_downgrade_lock(parent, AuLock_IR); +out_parent: + di_read_unlock(parent, AuLock_IR); + dput(parent); +out: + AuTraceErr(err); + return err; +} + +int au_do_open(struct file *file, struct au_do_open_args *args) +{ + int err, aopen = args->aopen; + struct dentry *dentry; + struct au_finfo *finfo; + + if (!aopen) + err = au_finfo_init(file, args->fidir); + else { + lockdep_off(); + err = au_finfo_init(file, args->fidir); + lockdep_on(); + } + if (unlikely(err)) + goto out; + + dentry = file->f_path.dentry; + AuDebugOn(IS_ERR_OR_NULL(dentry)); + di_write_lock_child(dentry); + err = au_cmoo(dentry); + di_downgrade_lock(dentry, AuLock_IR); + if (!err) { + if (!aopen) + err = args->open(file, vfsub_file_flags(file), NULL); + else { + lockdep_off(); + err = args->open(file, vfsub_file_flags(file), + args->h_file); + lockdep_on(); + } + } + di_read_unlock(dentry, AuLock_IR); + + finfo = au_fi(file); + if (!err) { + finfo->fi_file = file; + au_hbl_add(&finfo->fi_hlist, + &au_sbi(file->f_path.dentry->d_sb)->si_files); + } + if (!aopen) + fi_write_unlock(file); + else { + lockdep_off(); + fi_write_unlock(file); + lockdep_on(); + } + if (unlikely(err)) { + finfo->fi_hdir = NULL; + au_finfo_fin(file); + } + +out: + AuTraceErr(err); + return err; +} + +int au_reopen_nondir(struct file *file) +{ + int err; + aufs_bindex_t btop; + struct dentry *dentry; + struct au_branch *br; + struct file *h_file, *h_file_tmp; + + dentry = file->f_path.dentry; + btop = au_dbtop(dentry); + br = au_sbr(dentry->d_sb, btop); + h_file_tmp = NULL; + if (au_fbtop(file) == btop) { + h_file = au_hf_top(file); + if (file->f_mode == h_file->f_mode) + return 0; /* success */ + h_file_tmp = h_file; + get_file(h_file_tmp); + au_lcnt_inc(&br->br_nfiles); + au_set_h_fptr(file, btop, NULL); + } + AuDebugOn(au_fi(file)->fi_hdir); + /* + * it can happen + * file exists on both of rw and ro + * open --> dbtop and fbtop are both 0 + * prepend a branch as rw, "rw" become ro + * remove rw/file + * delete the top branch, "rw" becomes rw again + * --> dbtop is 1, fbtop is still 0 + * write --> fbtop is 0 but dbtop is 1 + */ + /* AuDebugOn(au_fbtop(file) < btop); */ + + h_file = au_h_open(dentry, btop, vfsub_file_flags(file) & ~O_TRUNC, + file, /*force_wr*/0); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) { + if (h_file_tmp) { + /* revert */ + au_set_h_fptr(file, btop, h_file_tmp); + h_file_tmp = NULL; + } + goto out; /* todo: close all? */ + } + + err = 0; + au_set_fbtop(file, btop); + au_set_h_fptr(file, btop, h_file); + au_update_figen(file); + /* todo: necessary? */ + /* file->f_ra = h_file->f_ra; */ + +out: + if (h_file_tmp) { + fput(h_file_tmp); + au_lcnt_dec(&br->br_nfiles); + } + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int au_reopen_wh(struct file *file, aufs_bindex_t btgt, + struct dentry *hi_wh) +{ + int err; + aufs_bindex_t btop; + struct au_dinfo *dinfo; + struct dentry *h_dentry; + struct au_hdentry *hdp; + + dinfo = au_di(file->f_path.dentry); + AuRwMustWriteLock(&dinfo->di_rwsem); + + btop = dinfo->di_btop; + dinfo->di_btop = btgt; + hdp = au_hdentry(dinfo, btgt); + h_dentry = hdp->hd_dentry; + hdp->hd_dentry = hi_wh; + err = au_reopen_nondir(file); + hdp->hd_dentry = h_dentry; + dinfo->di_btop = btop; + + return err; +} + +static int au_ready_to_write_wh(struct file *file, loff_t len, + aufs_bindex_t bcpup, struct au_pin *pin) +{ + int err; + struct inode *inode, *h_inode; + struct dentry *h_dentry, *hi_wh; + struct au_cp_generic cpg = { + .dentry = file->f_path.dentry, + .bdst = bcpup, + .bsrc = -1, + .len = len, + .pin = pin + }; + + au_update_dbtop(cpg.dentry); + inode = d_inode(cpg.dentry); + h_inode = NULL; + if (au_dbtop(cpg.dentry) <= bcpup + && au_dbbot(cpg.dentry) >= bcpup) { + h_dentry = au_h_dptr(cpg.dentry, bcpup); + if (h_dentry && d_is_positive(h_dentry)) + h_inode = d_inode(h_dentry); + } + hi_wh = au_hi_wh(inode, bcpup); + if (!hi_wh && !h_inode) + err = au_sio_cpup_wh(&cpg, file); + else + /* already copied-up after unlink */ + err = au_reopen_wh(file, bcpup, hi_wh); + + if (!err + && (inode->i_nlink > 1 + || (inode->i_state & I_LINKABLE)) + && au_opt_test(au_mntflags(cpg.dentry->d_sb), PLINK)) + au_plink_append(inode, bcpup, au_h_dptr(cpg.dentry, bcpup)); + + return err; +} + +/* + * prepare the @file for writing. + */ +int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin) +{ + int err; + aufs_bindex_t dbtop; + struct dentry *parent; + struct inode *inode; + struct super_block *sb; + struct file *h_file; + struct au_cp_generic cpg = { + .dentry = file->f_path.dentry, + .bdst = -1, + .bsrc = -1, + .len = len, + .pin = pin, + .flags = AuCpup_DTIME + }; + + sb = cpg.dentry->d_sb; + inode = d_inode(cpg.dentry); + cpg.bsrc = au_fbtop(file); + err = au_test_ro(sb, cpg.bsrc, inode); + if (!err && (au_hf_top(file)->f_mode & FMODE_WRITE)) { + err = au_pin(pin, cpg.dentry, cpg.bsrc, AuOpt_UDBA_NONE, + /*flags*/0); + goto out; + } + + /* need to cpup or reopen */ + parent = dget_parent(cpg.dentry); + di_write_lock_parent(parent); + err = AuWbrCopyup(au_sbi(sb), cpg.dentry); + cpg.bdst = err; + if (unlikely(err < 0)) + goto out_dgrade; + err = 0; + + if (!d_unhashed(cpg.dentry) && !au_h_dptr(parent, cpg.bdst)) { + err = au_cpup_dirs(cpg.dentry, cpg.bdst); + if (unlikely(err)) + goto out_dgrade; + } + + err = au_pin(pin, cpg.dentry, cpg.bdst, AuOpt_UDBA_NONE, + AuPin_DI_LOCKED | AuPin_MNT_WRITE); + if (unlikely(err)) + goto out_dgrade; + + dbtop = au_dbtop(cpg.dentry); + if (dbtop <= cpg.bdst) + cpg.bsrc = cpg.bdst; + + if (dbtop <= cpg.bdst /* just reopen */ + || !d_unhashed(cpg.dentry) /* copyup and reopen */ + ) { + h_file = au_h_open_pre(cpg.dentry, cpg.bsrc, /*force_wr*/0); + if (IS_ERR(h_file)) + err = PTR_ERR(h_file); + else { + di_downgrade_lock(parent, AuLock_IR); + if (dbtop > cpg.bdst) + err = au_sio_cpup_simple(&cpg); + if (!err) + err = au_reopen_nondir(file); + au_h_open_post(cpg.dentry, cpg.bsrc, h_file); + } + } else { /* copyup as wh and reopen */ + /* + * since writable hfsplus branch is not supported, + * h_open_pre/post() are unnecessary. + */ + err = au_ready_to_write_wh(file, len, cpg.bdst, pin); + di_downgrade_lock(parent, AuLock_IR); + } + + if (!err) { + au_pin_set_parent_lflag(pin, /*lflag*/0); + goto out_dput; /* success */ + } + au_unpin(pin); + goto out_unlock; + +out_dgrade: + di_downgrade_lock(parent, AuLock_IR); +out_unlock: + di_read_unlock(parent, AuLock_IR); +out_dput: + dput(parent); +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +int au_do_flush(struct file *file, fl_owner_t id, + int (*flush)(struct file *file, fl_owner_t id)) +{ + int err; + struct super_block *sb; + struct inode *inode; + + inode = file_inode(file); + sb = inode->i_sb; + si_noflush_read_lock(sb); + fi_read_lock(file); + ii_read_lock_child(inode); + + err = flush(file, id); + au_cpup_attr_timesizes(inode); + + ii_read_unlock(inode); + fi_read_unlock(file); + si_read_unlock(sb); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int au_file_refresh_by_inode(struct file *file, int *need_reopen) +{ + int err; + struct au_pin pin; + struct au_finfo *finfo; + struct dentry *parent, *hi_wh; + struct inode *inode; + struct super_block *sb; + struct au_cp_generic cpg = { + .dentry = file->f_path.dentry, + .bdst = -1, + .bsrc = -1, + .len = -1, + .pin = &pin, + .flags = AuCpup_DTIME + }; + + FiMustWriteLock(file); + + err = 0; + finfo = au_fi(file); + sb = cpg.dentry->d_sb; + inode = d_inode(cpg.dentry); + cpg.bdst = au_ibtop(inode); + if (cpg.bdst == finfo->fi_btop || IS_ROOT(cpg.dentry)) + goto out; + + parent = dget_parent(cpg.dentry); + if (au_test_ro(sb, cpg.bdst, inode)) { + di_read_lock_parent(parent, !AuLock_IR); + err = AuWbrCopyup(au_sbi(sb), cpg.dentry); + cpg.bdst = err; + di_read_unlock(parent, !AuLock_IR); + if (unlikely(err < 0)) + goto out_parent; + err = 0; + } + + di_read_lock_parent(parent, AuLock_IR); + hi_wh = au_hi_wh(inode, cpg.bdst); + if (!S_ISDIR(inode->i_mode) + && au_opt_test(au_mntflags(sb), PLINK) + && au_plink_test(inode) + && !d_unhashed(cpg.dentry) + && cpg.bdst < au_dbtop(cpg.dentry)) { + err = au_test_and_cpup_dirs(cpg.dentry, cpg.bdst); + if (unlikely(err)) + goto out_unlock; + + /* always superio. */ + err = au_pin(&pin, cpg.dentry, cpg.bdst, AuOpt_UDBA_NONE, + AuPin_DI_LOCKED | AuPin_MNT_WRITE); + if (!err) { + err = au_sio_cpup_simple(&cpg); + au_unpin(&pin); + } + } else if (hi_wh) { + /* already copied-up after unlink */ + err = au_reopen_wh(file, cpg.bdst, hi_wh); + *need_reopen = 0; + } + +out_unlock: + di_read_unlock(parent, AuLock_IR); +out_parent: + dput(parent); +out: + return err; +} + +static void au_do_refresh_dir(struct file *file) +{ + aufs_bindex_t bindex, bbot, new_bindex, brid; + struct au_hfile *p, tmp, *q; + struct au_finfo *finfo; + struct super_block *sb; + struct au_fidir *fidir; + + FiMustWriteLock(file); + + sb = file->f_path.dentry->d_sb; + finfo = au_fi(file); + fidir = finfo->fi_hdir; + AuDebugOn(!fidir); + p = fidir->fd_hfile + finfo->fi_btop; + brid = p->hf_br->br_id; + bbot = fidir->fd_bbot; + for (bindex = finfo->fi_btop; bindex <= bbot; bindex++, p++) { + if (!p->hf_file) + continue; + + new_bindex = au_br_index(sb, p->hf_br->br_id); + if (new_bindex == bindex) + continue; + if (new_bindex < 0) { + au_set_h_fptr(file, bindex, NULL); + continue; + } + + /* swap two lower inode, and loop again */ + q = fidir->fd_hfile + new_bindex; + tmp = *q; + *q = *p; + *p = tmp; + if (tmp.hf_file) { + bindex--; + p--; + } + } + + p = fidir->fd_hfile; + if (!au_test_mmapped(file) && !d_unlinked(file->f_path.dentry)) { + bbot = au_sbbot(sb); + for (finfo->fi_btop = 0; finfo->fi_btop <= bbot; + finfo->fi_btop++, p++) + if (p->hf_file) { + if (file_inode(p->hf_file)) + break; + au_hfput(p, /*execed*/0); + } + } else { + bbot = au_br_index(sb, brid); + for (finfo->fi_btop = 0; finfo->fi_btop < bbot; + finfo->fi_btop++, p++) + if (p->hf_file) + au_hfput(p, /*execed*/0); + bbot = au_sbbot(sb); + } + + p = fidir->fd_hfile + bbot; + for (fidir->fd_bbot = bbot; fidir->fd_bbot >= finfo->fi_btop; + fidir->fd_bbot--, p--) + if (p->hf_file) { + if (file_inode(p->hf_file)) + break; + au_hfput(p, /*execed*/0); + } + AuDebugOn(fidir->fd_bbot < finfo->fi_btop); +} + +/* + * after branch manipulating, refresh the file. + */ +static int refresh_file(struct file *file, int (*reopen)(struct file *file)) +{ + int err, need_reopen, nbr; + aufs_bindex_t bbot, bindex; + struct dentry *dentry; + struct super_block *sb; + struct au_finfo *finfo; + struct au_hfile *hfile; + + dentry = file->f_path.dentry; + sb = dentry->d_sb; + nbr = au_sbbot(sb) + 1; + finfo = au_fi(file); + if (!finfo->fi_hdir) { + hfile = &finfo->fi_htop; + AuDebugOn(!hfile->hf_file); + bindex = au_br_index(sb, hfile->hf_br->br_id); + AuDebugOn(bindex < 0); + if (bindex != finfo->fi_btop) + au_set_fbtop(file, bindex); + } else { + err = au_fidir_realloc(finfo, nbr, /*may_shrink*/0); + if (unlikely(err)) + goto out; + au_do_refresh_dir(file); + } + + err = 0; + need_reopen = 1; + if (!au_test_mmapped(file)) + err = au_file_refresh_by_inode(file, &need_reopen); + if (finfo->fi_hdir) + /* harmless if err */ + au_fidir_realloc(finfo, nbr, /*may_shrink*/1); + if (!err && need_reopen && !d_unlinked(dentry)) + err = reopen(file); + if (!err) { + au_update_figen(file); + goto out; /* success */ + } + + /* error, close all lower files */ + if (finfo->fi_hdir) { + bbot = au_fbbot_dir(file); + for (bindex = au_fbtop(file); bindex <= bbot; bindex++) + au_set_h_fptr(file, bindex, NULL); + } + +out: + return err; +} + +/* common function to regular file and dir */ +int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file), + int wlock, unsigned int fi_lsc) +{ + int err; + unsigned int sigen, figen; + aufs_bindex_t btop; + unsigned char pseudo_link; + struct dentry *dentry; + struct inode *inode; + + err = 0; + dentry = file->f_path.dentry; + inode = d_inode(dentry); + sigen = au_sigen(dentry->d_sb); + fi_write_lock_nested(file, fi_lsc); + figen = au_figen(file); + if (!fi_lsc) + di_write_lock_child(dentry); + else + di_write_lock_child2(dentry); + btop = au_dbtop(dentry); + pseudo_link = (btop != au_ibtop(inode)); + if (sigen == figen && !pseudo_link && au_fbtop(file) == btop) { + if (!wlock) { + di_downgrade_lock(dentry, AuLock_IR); + fi_downgrade_lock(file); + } + goto out; /* success */ + } + + AuDbg("sigen %d, figen %d\n", sigen, figen); + if (au_digen_test(dentry, sigen)) { + err = au_reval_dpath(dentry, sigen); + AuDebugOn(!err && au_digen_test(dentry, sigen)); + } + + if (!err) + err = refresh_file(file, reopen); + if (!err) { + if (!wlock) { + di_downgrade_lock(dentry, AuLock_IR); + fi_downgrade_lock(file); + } + } else { + di_write_unlock(dentry); + fi_write_unlock(file); + } + +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* cf. aufs_nopage() */ +/* for madvise(2) */ +static int aufs_readpage(struct file *file __maybe_unused, struct page *page) +{ + unlock_page(page); + return 0; +} + +/* it will never be called, but necessary to support O_DIRECT */ +static ssize_t aufs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) +{ BUG(); return 0; } + +/* they will never be called. */ +#ifdef CONFIG_AUFS_DEBUG +static int aufs_write_begin(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned flags, + struct page **pagep, void **fsdata) +{ AuUnsupport(); return 0; } +static int aufs_write_end(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct page *page, void *fsdata) +{ AuUnsupport(); return 0; } +static int aufs_writepage(struct page *page, struct writeback_control *wbc) +{ AuUnsupport(); return 0; } + +static int aufs_set_page_dirty(struct page *page) +{ AuUnsupport(); return 0; } +static void aufs_invalidatepage(struct page *page, unsigned int offset, + unsigned int length) +{ AuUnsupport(); } +static int aufs_releasepage(struct page *page, gfp_t gfp) +{ AuUnsupport(); return 0; } +#if 0 /* called by memory compaction regardless file */ +static int aufs_migratepage(struct address_space *mapping, struct page *newpage, + struct page *page, enum migrate_mode mode) +{ AuUnsupport(); return 0; } +#endif +static bool aufs_isolate_page(struct page *page, isolate_mode_t mode) +{ AuUnsupport(); return true; } +static void aufs_putback_page(struct page *page) +{ AuUnsupport(); } +static int aufs_launder_page(struct page *page) +{ AuUnsupport(); return 0; } +static int aufs_is_partially_uptodate(struct page *page, + unsigned long from, + unsigned long count) +{ AuUnsupport(); return 0; } +static void aufs_is_dirty_writeback(struct page *page, bool *dirty, + bool *writeback) +{ AuUnsupport(); } +static int aufs_error_remove_page(struct address_space *mapping, + struct page *page) +{ AuUnsupport(); return 0; } +static int aufs_swap_activate(struct swap_info_struct *sis, struct file *file, + sector_t *span) +{ AuUnsupport(); return 0; } +static void aufs_swap_deactivate(struct file *file) +{ AuUnsupport(); } +#endif /* CONFIG_AUFS_DEBUG */ + +const struct address_space_operations aufs_aop = { + .readpage = aufs_readpage, + .direct_IO = aufs_direct_IO, +#ifdef CONFIG_AUFS_DEBUG + .writepage = aufs_writepage, + /* no writepages, because of writepage */ + .set_page_dirty = aufs_set_page_dirty, + /* no readpages, because of readpage */ + .write_begin = aufs_write_begin, + .write_end = aufs_write_end, + /* no bmap, no block device */ + .invalidatepage = aufs_invalidatepage, + .releasepage = aufs_releasepage, + /* is fallback_migrate_page ok? */ + /* .migratepage = aufs_migratepage, */ + .isolate_page = aufs_isolate_page, + .putback_page = aufs_putback_page, + .launder_page = aufs_launder_page, + .is_partially_uptodate = aufs_is_partially_uptodate, + .is_dirty_writeback = aufs_is_dirty_writeback, + .error_remove_page = aufs_error_remove_page, + .swap_activate = aufs_swap_activate, + .swap_deactivate = aufs_swap_deactivate +#endif /* CONFIG_AUFS_DEBUG */ +}; diff --git a/fs/aufs/file.h b/fs/aufs/file.h new file mode 100644 index 0000000000000..ddaf01ae6d5b9 --- /dev/null +++ b/fs/aufs/file.h @@ -0,0 +1,342 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * file operations + */ + +#ifndef __AUFS_FILE_H__ +#define __AUFS_FILE_H__ + +#ifdef __KERNEL__ + +#include +#include +#include +#include +#include "rwsem.h" + +struct au_branch; +struct au_hfile { + struct file *hf_file; + struct au_branch *hf_br; +}; + +struct au_vdir; +struct au_fidir { + aufs_bindex_t fd_bbot; + aufs_bindex_t fd_nent; + struct au_vdir *fd_vdir_cache; + struct au_hfile fd_hfile[]; +}; + +static inline int au_fidir_sz(int nent) +{ + AuDebugOn(nent < 0); + return sizeof(struct au_fidir) + sizeof(struct au_hfile) * nent; +} + +struct au_finfo { + atomic_t fi_generation; + + struct au_rwsem fi_rwsem; + aufs_bindex_t fi_btop; + + /* do not union them */ + struct { /* for non-dir */ + struct au_hfile fi_htop; + atomic_t fi_mmapped; + }; + struct au_fidir *fi_hdir; /* for dir only */ + + struct hlist_bl_node fi_hlist; + struct file *fi_file; /* very ugly */ + struct rcu_head rcu; +} ____cacheline_aligned_in_smp; + +/* ---------------------------------------------------------------------- */ + +/* file.c */ +extern const struct address_space_operations aufs_aop; +unsigned int au_file_roflags(unsigned int flags); +struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags, + struct file *file, int force_wr); +struct au_do_open_args { + int aopen; + int (*open)(struct file *file, int flags, + struct file *h_file); + struct au_fidir *fidir; + struct file *h_file; +}; +int au_do_open(struct file *file, struct au_do_open_args *args); +int au_reopen_nondir(struct file *file); +struct au_pin; +int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin); +int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file), + int wlock, unsigned int fi_lsc); +int au_do_flush(struct file *file, fl_owner_t id, + int (*flush)(struct file *file, fl_owner_t id)); + +/* poll.c */ +#ifdef CONFIG_AUFS_POLL +__poll_t aufs_poll(struct file *file, struct poll_table_struct *pt); +#endif + +#ifdef CONFIG_AUFS_BR_HFSPLUS +/* hfsplus.c */ +struct file *au_h_open_pre(struct dentry *dentry, aufs_bindex_t bindex, + int force_wr); +void au_h_open_post(struct dentry *dentry, aufs_bindex_t bindex, + struct file *h_file); +#else +AuStub(struct file *, au_h_open_pre, return NULL, struct dentry *dentry, + aufs_bindex_t bindex, int force_wr) +AuStubVoid(au_h_open_post, struct dentry *dentry, aufs_bindex_t bindex, + struct file *h_file); +#endif + +/* f_op.c */ +extern const struct file_operations aufs_file_fop; +int au_do_open_nondir(struct file *file, int flags, struct file *h_file); +int aufs_release_nondir(struct inode *inode __maybe_unused, struct file *file); +struct file *au_read_pre(struct file *file, int keep_fi, unsigned int lsc); + +/* finfo.c */ +void au_hfput(struct au_hfile *hf, int execed); +void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, + struct file *h_file); + +void au_update_figen(struct file *file); +struct au_fidir *au_fidir_alloc(struct super_block *sb); +int au_fidir_realloc(struct au_finfo *finfo, int nbr, int may_shrink); + +void au_fi_init_once(void *_fi); +void au_finfo_fin(struct file *file); +int au_finfo_init(struct file *file, struct au_fidir *fidir); + +/* ioctl.c */ +long aufs_ioctl_nondir(struct file *file, unsigned int cmd, unsigned long arg); +#ifdef CONFIG_COMPAT +long aufs_compat_ioctl_dir(struct file *file, unsigned int cmd, + unsigned long arg); +long aufs_compat_ioctl_nondir(struct file *file, unsigned int cmd, + unsigned long arg); +#endif + +/* ---------------------------------------------------------------------- */ + +static inline struct au_finfo *au_fi(struct file *file) +{ + return file->private_data; +} + +/* ---------------------------------------------------------------------- */ + +#define fi_read_lock(f) au_rw_read_lock(&au_fi(f)->fi_rwsem) +#define fi_write_lock(f) au_rw_write_lock(&au_fi(f)->fi_rwsem) +#define fi_read_trylock(f) au_rw_read_trylock(&au_fi(f)->fi_rwsem) +#define fi_write_trylock(f) au_rw_write_trylock(&au_fi(f)->fi_rwsem) +/* +#define fi_read_trylock_nested(f) \ + au_rw_read_trylock_nested(&au_fi(f)->fi_rwsem) +#define fi_write_trylock_nested(f) \ + au_rw_write_trylock_nested(&au_fi(f)->fi_rwsem) +*/ + +#define fi_read_unlock(f) au_rw_read_unlock(&au_fi(f)->fi_rwsem) +#define fi_write_unlock(f) au_rw_write_unlock(&au_fi(f)->fi_rwsem) +#define fi_downgrade_lock(f) au_rw_dgrade_lock(&au_fi(f)->fi_rwsem) + +/* lock subclass for finfo */ +enum { + AuLsc_FI_1, + AuLsc_FI_2 +}; + +static inline void fi_read_lock_nested(struct file *f, unsigned int lsc) +{ + au_rw_read_lock_nested(&au_fi(f)->fi_rwsem, lsc); +} + +static inline void fi_write_lock_nested(struct file *f, unsigned int lsc) +{ + au_rw_write_lock_nested(&au_fi(f)->fi_rwsem, lsc); +} + +/* + * fi_read_lock_1, fi_write_lock_1, + * fi_read_lock_2, fi_write_lock_2 + */ +#define AuReadLockFunc(name) \ +static inline void fi_read_lock_##name(struct file *f) \ +{ fi_read_lock_nested(f, AuLsc_FI_##name); } + +#define AuWriteLockFunc(name) \ +static inline void fi_write_lock_##name(struct file *f) \ +{ fi_write_lock_nested(f, AuLsc_FI_##name); } + +#define AuRWLockFuncs(name) \ + AuReadLockFunc(name) \ + AuWriteLockFunc(name) + +AuRWLockFuncs(1); +AuRWLockFuncs(2); + +#undef AuReadLockFunc +#undef AuWriteLockFunc +#undef AuRWLockFuncs + +#define FiMustNoWaiters(f) AuRwMustNoWaiters(&au_fi(f)->fi_rwsem) +#define FiMustAnyLock(f) AuRwMustAnyLock(&au_fi(f)->fi_rwsem) +#define FiMustWriteLock(f) AuRwMustWriteLock(&au_fi(f)->fi_rwsem) + +/* ---------------------------------------------------------------------- */ + +/* todo: hard/soft set? */ +static inline aufs_bindex_t au_fbtop(struct file *file) +{ + FiMustAnyLock(file); + return au_fi(file)->fi_btop; +} + +static inline aufs_bindex_t au_fbbot_dir(struct file *file) +{ + FiMustAnyLock(file); + AuDebugOn(!au_fi(file)->fi_hdir); + return au_fi(file)->fi_hdir->fd_bbot; +} + +static inline struct au_vdir *au_fvdir_cache(struct file *file) +{ + FiMustAnyLock(file); + AuDebugOn(!au_fi(file)->fi_hdir); + return au_fi(file)->fi_hdir->fd_vdir_cache; +} + +static inline void au_set_fbtop(struct file *file, aufs_bindex_t bindex) +{ + FiMustWriteLock(file); + au_fi(file)->fi_btop = bindex; +} + +static inline void au_set_fbbot_dir(struct file *file, aufs_bindex_t bindex) +{ + FiMustWriteLock(file); + AuDebugOn(!au_fi(file)->fi_hdir); + au_fi(file)->fi_hdir->fd_bbot = bindex; +} + +static inline void au_set_fvdir_cache(struct file *file, + struct au_vdir *vdir_cache) +{ + FiMustWriteLock(file); + AuDebugOn(!au_fi(file)->fi_hdir); + au_fi(file)->fi_hdir->fd_vdir_cache = vdir_cache; +} + +static inline struct file *au_hf_top(struct file *file) +{ + FiMustAnyLock(file); + AuDebugOn(au_fi(file)->fi_hdir); + return au_fi(file)->fi_htop.hf_file; +} + +static inline struct file *au_hf_dir(struct file *file, aufs_bindex_t bindex) +{ + FiMustAnyLock(file); + AuDebugOn(!au_fi(file)->fi_hdir); + return au_fi(file)->fi_hdir->fd_hfile[0 + bindex].hf_file; +} + +/* todo: memory barrier? */ +static inline unsigned int au_figen(struct file *f) +{ + return atomic_read(&au_fi(f)->fi_generation); +} + +static inline void au_set_mmapped(struct file *f) +{ + if (atomic_inc_return(&au_fi(f)->fi_mmapped)) + return; + pr_warn("fi_mmapped wrapped around\n"); + while (!atomic_inc_return(&au_fi(f)->fi_mmapped)) + ; +} + +static inline void au_unset_mmapped(struct file *f) +{ + atomic_dec(&au_fi(f)->fi_mmapped); +} + +static inline int au_test_mmapped(struct file *f) +{ + return atomic_read(&au_fi(f)->fi_mmapped); +} + +/* customize vma->vm_file */ + +static inline void au_do_vm_file_reset(struct vm_area_struct *vma, + struct file *file) +{ + struct file *f; + + f = vma->vm_file; + get_file(file); + vma->vm_file = file; + fput(f); +} + +#ifdef CONFIG_MMU +#define AuDbgVmRegion(file, vma) do {} while (0) + +static inline void au_vm_file_reset(struct vm_area_struct *vma, + struct file *file) +{ + au_do_vm_file_reset(vma, file); +} +#else +#define AuDbgVmRegion(file, vma) \ + AuDebugOn((vma)->vm_region && (vma)->vm_region->vm_file != (file)) + +static inline void au_vm_file_reset(struct vm_area_struct *vma, + struct file *file) +{ + struct file *f; + + au_do_vm_file_reset(vma, file); + f = vma->vm_region->vm_file; + get_file(file); + vma->vm_region->vm_file = file; + fput(f); +} +#endif /* CONFIG_MMU */ + +/* handle vma->vm_prfile */ +static inline void au_vm_prfile_set(struct vm_area_struct *vma, + struct file *file) +{ + get_file(file); + vma->vm_prfile = file; +#ifndef CONFIG_MMU + get_file(file); + vma->vm_region->vm_prfile = file; +#endif +} + +#endif /* __KERNEL__ */ +#endif /* __AUFS_FILE_H__ */ diff --git a/fs/aufs/finfo.c b/fs/aufs/finfo.c new file mode 100644 index 0000000000000..73e1be63e03ae --- /dev/null +++ b/fs/aufs/finfo.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * file private data + */ + +#include "aufs.h" + +void au_hfput(struct au_hfile *hf, int execed) +{ + if (execed) + allow_write_access(hf->hf_file); + fput(hf->hf_file); + hf->hf_file = NULL; + au_lcnt_dec(&hf->hf_br->br_nfiles); + hf->hf_br = NULL; +} + +void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *val) +{ + struct au_finfo *finfo = au_fi(file); + struct au_hfile *hf; + struct au_fidir *fidir; + + fidir = finfo->fi_hdir; + if (!fidir) { + AuDebugOn(finfo->fi_btop != bindex); + hf = &finfo->fi_htop; + } else + hf = fidir->fd_hfile + bindex; + + if (hf && hf->hf_file) + au_hfput(hf, vfsub_file_execed(file)); + if (val) { + FiMustWriteLock(file); + AuDebugOn(IS_ERR_OR_NULL(file->f_path.dentry)); + hf->hf_file = val; + hf->hf_br = au_sbr(file->f_path.dentry->d_sb, bindex); + } +} + +void au_update_figen(struct file *file) +{ + atomic_set(&au_fi(file)->fi_generation, au_digen(file->f_path.dentry)); + /* smp_mb(); */ /* atomic_set */ +} + +/* ---------------------------------------------------------------------- */ + +struct au_fidir *au_fidir_alloc(struct super_block *sb) +{ + struct au_fidir *fidir; + int nbr; + + nbr = au_sbbot(sb) + 1; + if (nbr < 2) + nbr = 2; /* initial allocate for 2 branches */ + fidir = kzalloc(au_fidir_sz(nbr), GFP_NOFS); + if (fidir) { + fidir->fd_bbot = -1; + fidir->fd_nent = nbr; + } + + return fidir; +} + +int au_fidir_realloc(struct au_finfo *finfo, int nbr, int may_shrink) +{ + int err; + struct au_fidir *fidir, *p; + + AuRwMustWriteLock(&finfo->fi_rwsem); + fidir = finfo->fi_hdir; + AuDebugOn(!fidir); + + err = -ENOMEM; + p = au_kzrealloc(fidir, au_fidir_sz(fidir->fd_nent), au_fidir_sz(nbr), + GFP_NOFS, may_shrink); + if (p) { + p->fd_nent = nbr; + finfo->fi_hdir = p; + err = 0; + } + + return err; +} + +/* ---------------------------------------------------------------------- */ + +void au_finfo_fin(struct file *file) +{ + struct au_finfo *finfo; + + au_lcnt_dec(&au_sbi(file->f_path.dentry->d_sb)->si_nfiles); + + finfo = au_fi(file); + AuDebugOn(finfo->fi_hdir); + AuRwDestroy(&finfo->fi_rwsem); + au_cache_free_finfo(finfo); +} + +void au_fi_init_once(void *_finfo) +{ + struct au_finfo *finfo = _finfo; + + au_rw_init(&finfo->fi_rwsem); +} + +int au_finfo_init(struct file *file, struct au_fidir *fidir) +{ + int err; + struct au_finfo *finfo; + struct dentry *dentry; + + err = -ENOMEM; + dentry = file->f_path.dentry; + finfo = au_cache_alloc_finfo(); + if (unlikely(!finfo)) + goto out; + + err = 0; + au_lcnt_inc(&au_sbi(dentry->d_sb)->si_nfiles); + au_rw_write_lock(&finfo->fi_rwsem); + finfo->fi_btop = -1; + finfo->fi_hdir = fidir; + atomic_set(&finfo->fi_generation, au_digen(dentry)); + /* smp_mb(); */ /* atomic_set */ + + file->private_data = finfo; + +out: + return err; +} diff --git a/fs/aufs/fsctx.c b/fs/aufs/fsctx.c new file mode 100644 index 0000000000000..c4b78ee8e2a29 --- /dev/null +++ b/fs/aufs/fsctx.c @@ -0,0 +1,1242 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * fs context, aka new mount api + */ + +#include +#include "aufs.h" + +struct au_fsctx_opts { + aufs_bindex_t bindex; + unsigned char skipped; + struct au_opt *opt, *opt_tail; + struct super_block *sb; + struct au_sbinfo *sbinfo; + struct au_opts opts; +}; + +/* stop extra interpretation of errno in mount(8), and strange error messages */ +static int cvt_err(int err) +{ + AuTraceErr(err); + + switch (err) { + case -ENOENT: + case -ENOTDIR: + case -EEXIST: + case -EIO: + err = -EINVAL; + } + return err; +} + +static int au_fsctx_reconfigure(struct fs_context *fc) +{ + int err, do_dx; + unsigned int mntflags; + struct dentry *root; + struct super_block *sb; + struct inode *inode; + struct au_fsctx_opts *a = fc->fs_private; + + AuDbg("fc %p\n", fc); + + root = fc->root; + sb = root->d_sb; + err = si_write_lock(sb, AuLock_FLUSH | AuLock_NOPLM); + if (!err) { + di_write_lock_child(root); + err = au_opts_verify(sb, fc->sb_flags, /*pending*/0); + aufs_write_unlock(root); + } + + inode = d_inode(root); + inode_lock(inode); + err = si_write_lock(sb, AuLock_FLUSH | AuLock_NOPLM); + if (unlikely(err)) + goto out; + di_write_lock_child(root); + + /* au_opts_remount() may return an error */ + err = au_opts_remount(sb, &a->opts); + + if (au_ftest_opts(a->opts.flags, REFRESH)) + au_remount_refresh(sb, au_ftest_opts(a->opts.flags, + REFRESH_IDOP)); + + if (au_ftest_opts(a->opts.flags, REFRESH_DYAOP)) { + mntflags = au_mntflags(sb); + do_dx = !!au_opt_test(mntflags, DIO); + au_dy_arefresh(do_dx); + } + + au_fhsm_wrote_all(sb, /*force*/1); /* ?? */ + aufs_write_unlock(root); + +out: + inode_unlock(inode); + err = cvt_err(err); + AuTraceErr(err); + + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int au_fsctx_fill_super(struct super_block *sb, struct fs_context *fc) +{ + int err; + struct au_fsctx_opts *a = fc->fs_private; + struct au_sbinfo *sbinfo = a->sbinfo; + struct dentry *root; + struct inode *inode; + + sbinfo->si_sb = sb; + sb->s_fs_info = sbinfo; + kobject_get(&sbinfo->si_kobj); + + __si_write_lock(sb); + si_pid_set(sb); + au_sbilist_add(sb); + + /* all timestamps always follow the ones on the branch */ + sb->s_flags |= SB_NOATIME | SB_NODIRATIME; + sb->s_flags |= SB_I_VERSION; /* do we really need this? */ + sb->s_op = &aufs_sop; + sb->s_d_op = &aufs_dop; + sb->s_magic = AUFS_SUPER_MAGIC; + sb->s_maxbytes = 0; + sb->s_stack_depth = 1; + au_export_init(sb); + au_xattr_init(sb); + + err = au_alloc_root(sb); + if (unlikely(err)) { + si_write_unlock(sb); + goto out; + } + root = sb->s_root; + inode = d_inode(root); + ii_write_lock_parent(inode); + aufs_write_unlock(root); + + /* lock vfs_inode first, then aufs. */ + inode_lock(inode); + aufs_write_lock(root); + err = au_opts_mount(sb, &a->opts); + AuTraceErr(err); + if (!err && au_ftest_si(sbinfo, NO_DREVAL)) { + sb->s_d_op = &aufs_dop_noreval; + /* infofc(fc, "%ps", sb->s_d_op); */ + pr_info("%ps\n", sb->s_d_op); + au_refresh_dop(root, /*force_reval*/0); + sbinfo->si_iop_array = aufs_iop_nogetattr; + au_refresh_iop(inode, /*force_getattr*/0); + } + aufs_write_unlock(root); + inode_unlock(inode); + if (!err) + goto out; /* success */ + + dput(root); + sb->s_root = NULL; + +out: + if (unlikely(err)) + kobject_put(&sbinfo->si_kobj); + AuTraceErr(err); + err = cvt_err(err); + AuTraceErr(err); + return err; +} + +static int au_fsctx_get_tree(struct fs_context *fc) +{ + int err; + + AuDbg("fc %p\n", fc); + err = get_tree_nodev(fc, au_fsctx_fill_super); + + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static void au_fsctx_dump(struct au_opts *opts) +{ +#ifdef CONFIG_AUFS_DEBUG + /* reduce stack space */ + union { + struct au_opt_add *add; + struct au_opt_del *del; + struct au_opt_mod *mod; + struct au_opt_xino *xino; + struct au_opt_xino_itrunc *xino_itrunc; + struct au_opt_wbr_create *create; + } u; + struct au_opt *opt; + + opt = opts->opt; + while (opt->type != Opt_tail) { + switch (opt->type) { + case Opt_add: + u.add = &opt->add; + AuDbg("add {b%d, %s, 0x%x, %p}\n", + u.add->bindex, u.add->pathname, u.add->perm, + u.add->path.dentry); + break; + case Opt_del: + fallthrough; + case Opt_idel: + u.del = &opt->del; + AuDbg("del {%s, %p}\n", + u.del->pathname, u.del->h_path.dentry); + break; + case Opt_mod: + fallthrough; + case Opt_imod: + u.mod = &opt->mod; + AuDbg("mod {%s, 0x%x, %p}\n", + u.mod->path, u.mod->perm, u.mod->h_root); + break; + case Opt_append: + u.add = &opt->add; + AuDbg("append {b%d, %s, 0x%x, %p}\n", + u.add->bindex, u.add->pathname, u.add->perm, + u.add->path.dentry); + break; + case Opt_prepend: + u.add = &opt->add; + AuDbg("prepend {b%d, %s, 0x%x, %p}\n", + u.add->bindex, u.add->pathname, u.add->perm, + u.add->path.dentry); + break; + + case Opt_dirwh: + AuDbg("dirwh %d\n", opt->dirwh); + break; + case Opt_rdcache: + AuDbg("rdcache %d\n", opt->rdcache); + break; + case Opt_rdblk: + AuDbg("rdblk %d\n", opt->rdblk); + break; + case Opt_rdhash: + AuDbg("rdhash %u\n", opt->rdhash); + break; + + case Opt_xino: + u.xino = &opt->xino; + AuDbg("xino {%s %pD}\n", u.xino->path, u.xino->file); + break; + +#define au_fsctx_TF(name) \ + case Opt_##name: \ + if (opt->tf) \ + AuLabel(name); \ + else \ + AuLabel(no##name); \ + break; + + /* simple true/false flag */ + au_fsctx_TF(trunc_xino); + au_fsctx_TF(trunc_xib); + au_fsctx_TF(dirperm1); + au_fsctx_TF(plink); + au_fsctx_TF(shwh); + au_fsctx_TF(dio); + au_fsctx_TF(warn_perm); + au_fsctx_TF(verbose); + au_fsctx_TF(sum); + au_fsctx_TF(dirren); + au_fsctx_TF(acl); +#undef au_fsctx_TF + + case Opt_trunc_xino_path: + fallthrough; + case Opt_itrunc_xino: + u.xino_itrunc = &opt->xino_itrunc; + AuDbg("trunc_xino %d\n", u.xino_itrunc->bindex); + break; + case Opt_noxino: + AuLabel(noxino); + break; + + case Opt_list_plink: + AuLabel(list_plink); + break; + case Opt_udba: + AuDbg("udba %d, %s\n", + opt->udba, au_optstr_udba(opt->udba)); + break; + case Opt_diropq_a: + AuLabel(diropq_a); + break; + case Opt_diropq_w: + AuLabel(diropq_w); + break; + case Opt_wsum: + AuLabel(wsum); + break; + case Opt_wbr_create: + u.create = &opt->wbr_create; + AuDbg("create %d, %s\n", u.create->wbr_create, + au_optstr_wbr_create(u.create->wbr_create)); + switch (u.create->wbr_create) { + case AuWbrCreate_MFSV: + fallthrough; + case AuWbrCreate_PMFSV: + AuDbg("%d sec\n", u.create->mfs_second); + break; + case AuWbrCreate_MFSRR: + fallthrough; + case AuWbrCreate_TDMFS: + AuDbg("%llu watermark\n", + u.create->mfsrr_watermark); + break; + case AuWbrCreate_MFSRRV: + fallthrough; + case AuWbrCreate_TDMFSV: + fallthrough; + case AuWbrCreate_PMFSRRV: + AuDbg("%llu watermark, %d sec\n", + u.create->mfsrr_watermark, + u.create->mfs_second); + break; + } + break; + case Opt_wbr_copyup: + AuDbg("copyup %d, %s\n", opt->wbr_copyup, + au_optstr_wbr_copyup(opt->wbr_copyup)); + break; + case Opt_fhsm_sec: + AuDbg("fhsm_sec %u\n", opt->fhsm_second); + break; + + default: + AuDbg("type %d\n", opt->type); + BUG(); + } + opt++; + } +#endif +} + +/* ---------------------------------------------------------------------- */ + +/* + * For conditionally compiled mount options. + * Instead of fsparam_flag_no(), use this macro to distinguish ignore_silent. + */ +#define au_ignore_flag(name, action) \ + fsparam_flag(name, action), \ + fsparam_flag("no" name, Opt_ignore_silent) + +const struct fs_parameter_spec aufs_fsctx_paramspec[] = { + fsparam_string("br", Opt_br), + + /* "add=%d:%s" or "ins=%d:%s" */ + fsparam_string("add", Opt_add), + fsparam_string("ins", Opt_add), + fsparam_path("append", Opt_append), + fsparam_path("prepend", Opt_prepend), + + fsparam_path("del", Opt_del), + /* fsparam_s32("idel", Opt_idel), */ + fsparam_path("mod", Opt_mod), + /* fsparam_string("imod", Opt_imod), */ + + fsparam_s32("dirwh", Opt_dirwh), + + fsparam_path("xino", Opt_xino), + fsparam_flag("noxino", Opt_noxino), + fsparam_flag_no("trunc_xino", Opt_trunc_xino), + /* "trunc_xino_v=%d:%d" */ + /* fsparam_string("trunc_xino_v", Opt_trunc_xino_v), */ + fsparam_path("trunc_xino", Opt_trunc_xino_path), + fsparam_s32("itrunc_xino", Opt_itrunc_xino), + /* fsparam_path("zxino", Opt_zxino), */ + fsparam_flag_no("trunc_xib", Opt_trunc_xib), + +#ifdef CONFIG_PROC_FS + fsparam_flag_no("plink", Opt_plink), +#else + au_ignore_flag("plink", Opt_ignore), +#endif + +#ifdef CONFIG_AUFS_DEBUG + fsparam_flag("list_plink", Opt_list_plink), +#endif + + fsparam_string("udba", Opt_udba), + + fsparam_flag_no("dio", Opt_dio), + +#ifdef CONFIG_AUFS_DIRREN + fsparam_flag_no("dirren", Opt_dirren), +#else + au_ignore_flag("dirren", Opt_ignore), +#endif + +#ifdef CONFIG_AUFS_FHSM + fsparam_s32("fhsm_sec", Opt_fhsm_sec), +#else + fsparam_s32("fhsm_sec", Opt_ignore), +#endif + + /* always | a | whiteouted | w */ + fsparam_string("diropq", Opt_diropq), + + fsparam_flag_no("warn_perm", Opt_warn_perm), + +#ifdef CONFIG_AUFS_SHWH + fsparam_flag_no("shwh", Opt_shwh), +#else + au_ignore_flag("shwh", Opt_err), +#endif + + fsparam_flag_no("dirperm1", Opt_dirperm1), + + fsparam_flag_no("verbose", Opt_verbose), + fsparam_flag("v", Opt_verbose), + fsparam_flag("quiet", Opt_noverbose), + fsparam_flag("q", Opt_noverbose), + /* user-space may handle this */ + fsparam_flag("silent", Opt_noverbose), + + fsparam_flag_no("sum", Opt_sum), + fsparam_flag("wsum", Opt_wsum), + + fsparam_s32("rdcache", Opt_rdcache), + /* "def" or s32 */ + fsparam_string("rdblk", Opt_rdblk), + /* "def" or s32 */ + fsparam_string("rdhash", Opt_rdhash), + + fsparam_string("create", Opt_wbr_create), + fsparam_string("create_policy", Opt_wbr_create), + fsparam_string("cpup", Opt_wbr_copyup), + fsparam_string("copyup", Opt_wbr_copyup), + fsparam_string("copyup_policy", Opt_wbr_copyup), + + /* generic VFS flag */ +#ifdef CONFIG_FS_POSIX_ACL + fsparam_flag_no("acl", Opt_acl), +#else + au_ignore_flag("acl", Opt_ignore), +#endif + + /* internal use for the scripts */ + fsparam_string("si", Opt_ignore_silent), + + /* obsoleted, keep them temporary */ + fsparam_flag("nodlgt", Opt_ignore_silent), + fsparam_flag("clean_plink", Opt_ignore), + fsparam_string("dirs", Opt_br), + fsparam_u32("debug", Opt_ignore), + /* "whiteout" or "all" */ + fsparam_string("delete", Opt_ignore), + fsparam_string("imap", Opt_ignore), + + /* temporary workaround, due to old mount(8)? */ + fsparam_flag("relatime", Opt_ignore_silent), + + {} +}; + +static int au_fsctx_parse_do_add(struct fs_context *fc, struct au_opt *opt, + char *brspec, size_t speclen, + aufs_bindex_t bindex) +{ + int err; + char *p; + + AuDbg("brspec %s\n", brspec); + + err = -ENOMEM; + if (!speclen) + speclen = strlen(brspec); + /* will be freed by au_fsctx_free() */ + p = kmemdup_nul(brspec, speclen, GFP_NOFS); + if (unlikely(!p)) { + errorfc(fc, "failed in %s", brspec); + goto out; + } + err = au_opt_add(opt, p, fc->sb_flags, bindex); + +out: + AuTraceErr(err); + return err; +} + +static int au_fsctx_parse_br(struct fs_context *fc, char *brspec) +{ + int err; + char *p; + struct au_fsctx_opts *a = fc->fs_private; + struct au_opt *opt = a->opt; + aufs_bindex_t bindex = a->bindex; + + AuDbg("brspec %s\n", brspec); + + err = -EINVAL; + while ((p = strsep(&brspec, ":")) && *p) { + err = au_fsctx_parse_do_add(fc, opt, p, /*len*/0, bindex); + AuTraceErr(err); + if (unlikely(err)) + break; + bindex++; + opt++; + if (unlikely(opt > a->opt_tail)) { + err = -E2BIG; + bindex--; + opt--; + break; + } + opt->type = Opt_tail; + a->skipped = 1; + } + a->bindex = bindex; + a->opt = opt; + + AuTraceErr(err); + return err; +} + +static int au_fsctx_parse_add(struct fs_context *fc, char *addspec) +{ + int err, n; + char *p; + struct au_fsctx_opts *a = fc->fs_private; + struct au_opt *opt = a->opt; + + err = -EINVAL; + p = strchr(addspec, ':'); + if (unlikely(!p)) { + errorfc(fc, "bad arg in %s", addspec); + goto out; + } + *p++ = '\0'; + err = kstrtoint(addspec, 0, &n); + if (unlikely(err)) { + errorfc(fc, "bad integer in %s", addspec); + goto out; + } + AuDbg("n %d\n", n); + err = au_fsctx_parse_do_add(fc, opt, p, /*len*/0, n); + +out: + AuTraceErr(err); + return err; +} + +static int au_fsctx_parse_del(struct fs_context *fc, struct au_opt_del *del, + struct fs_parameter *param) +{ + int err; + + err = -ENOMEM; + /* will be freed by au_fsctx_free() */ + del->pathname = kmemdup_nul(param->string, param->size, GFP_NOFS); + if (unlikely(!del->pathname)) + goto out; + AuDbg("del %s\n", del->pathname); + err = vfsub_kern_path(del->pathname, AuOpt_LkupDirFlags, &del->h_path); + if (unlikely(err)) + errorfc(fc, "lookup failed %s (%d)", del->pathname, err); + +out: + AuTraceErr(err); + return err; +} + +#if 0 /* reserved for future use */ +static int au_fsctx_parse_idel(struct fs_context *fc, struct au_opt_del *del, + aufs_bindex_t bindex) +{ + int err; + struct super_block *sb; + struct dentry *root; + struct au_fsctx_opts *a = fc->fs_private; + + sb = a->sb; + AuDebugOn(!sb); + + err = -EINVAL; + root = sb->s_root; + aufs_read_lock(root, AuLock_FLUSH); + if (bindex < 0 || au_sbbot(sb) < bindex) { + errorfc(fc, "out of bounds, %d", bindex); + goto out; + } + + err = 0; + del->h_path.dentry = dget(au_h_dptr(root, bindex)); + del->h_path.mnt = mntget(au_sbr_mnt(sb, bindex)); + +out: + aufs_read_unlock(root, !AuLock_IR); + AuTraceErr(err); + return err; +} +#endif + +static int au_fsctx_parse_mod(struct fs_context *fc, struct au_opt_mod *mod, + struct fs_parameter *param) +{ + int err; + struct path path; + char *p; + + err = -ENOMEM; + /* will be freed by au_fsctx_free() */ + mod->path = kmemdup_nul(param->string, param->size, GFP_NOFS); + if (unlikely(!mod->path)) + goto out; + + err = -EINVAL; + p = strchr(mod->path, '='); + if (unlikely(!p)) { + errorfc(fc, "no permission %s", mod->path); + goto out; + } + + *p++ = 0; + err = vfsub_kern_path(mod->path, AuOpt_LkupDirFlags, &path); + if (unlikely(err)) { + errorfc(fc, "lookup failed %s (%d)", mod->path, err); + goto out; + } + + mod->perm = au_br_perm_val(p); + AuDbg("mod path %s, perm 0x%x, %s", mod->path, mod->perm, p); + mod->h_root = dget(path.dentry); + path_put(&path); + +out: + AuTraceErr(err); + return err; +} + +#if 0 /* reserved for future use */ +static int au_fsctx_parse_imod(struct fs_context *fc, struct au_opt_mod *mod, + char *ibrspec) +{ + int err, n; + char *p; + struct super_block *sb; + struct dentry *root; + struct au_fsctx_opts *a = fc->fs_private; + + sb = a->sb; + AuDebugOn(!sb); + + err = -EINVAL; + p = strchr(ibrspec, ':'); + if (unlikely(!p)) { + errorfc(fc, "no index, %s", ibrspec); + goto out; + } + *p++ = '\0'; + err = kstrtoint(ibrspec, 0, &n); + if (unlikely(err)) { + errorfc(fc, "bad integer in %s", ibrspec); + goto out; + } + AuDbg("n %d\n", n); + + root = sb->s_root; + aufs_read_lock(root, AuLock_FLUSH); + if (n < 0 || au_sbbot(sb) < n) { + errorfc(fc, "out of bounds, %d", bindex); + goto out_root; + } + + err = 0; + mod->perm = au_br_perm_val(p); + AuDbg("mod path %s, perm 0x%x, %s\n", + mod->path, mod->perm, p); + mod->h_root = dget(au_h_dptr(root, bindex)); + +out_root: + aufs_read_unlock(root, !AuLock_IR); +out: + AuTraceErr(err); + return err; +} +#endif + +static int au_fsctx_parse_xino(struct fs_context *fc, + struct au_opt_xino *xino, + struct fs_parameter *param) +{ + int err; + struct au_fsctx_opts *a = fc->fs_private; + + err = -ENOMEM; + /* will be freed by au_opts_free() */ + xino->path = kmemdup_nul(param->string, param->size, GFP_NOFS); + if (unlikely(!xino->path)) + goto out; + AuDbg("path %s\n", xino->path); + + xino->file = au_xino_create(a->sb, xino->path, /*silent*/0, + /*wbrtop*/0); + err = PTR_ERR(xino->file); + if (IS_ERR(xino->file)) { + xino->file = NULL; + goto out; + } + + err = 0; + if (unlikely(a->sb && xino->file->f_path.dentry->d_sb == a->sb)) { + err = -EINVAL; + errorfc(fc, "%s must be outside", xino->path); + } + +out: + AuTraceErr(err); + return err; +} + +static +int au_fsctx_parse_xino_itrunc_path(struct fs_context *fc, + struct au_opt_xino_itrunc *xino_itrunc, + char *pathname) +{ + int err; + aufs_bindex_t bbot, bindex; + struct path path; + struct dentry *root; + struct au_fsctx_opts *a = fc->fs_private; + + AuDebugOn(!a->sb); + + err = vfsub_kern_path(pathname, AuOpt_LkupDirFlags, &path); + if (unlikely(err)) { + errorfc(fc, "lookup failed %s (%d)", pathname, err); + goto out; + } + + xino_itrunc->bindex = -1; + root = a->sb->s_root; + aufs_read_lock(root, AuLock_FLUSH); + bbot = au_sbbot(a->sb); + for (bindex = 0; bindex <= bbot; bindex++) { + if (au_h_dptr(root, bindex) == path.dentry) { + xino_itrunc->bindex = bindex; + break; + } + } + aufs_read_unlock(root, !AuLock_IR); + path_put(&path); + + if (unlikely(xino_itrunc->bindex < 0)) { + err = -EINVAL; + errorfc(fc, "no such branch %s", pathname); + } + +out: + AuTraceErr(err); + return err; +} + +static int au_fsctx_parse_xino_itrunc(struct fs_context *fc, + struct au_opt_xino_itrunc *xino_itrunc, + unsigned int bindex) +{ + int err; + aufs_bindex_t bbot; + struct super_block *sb; + struct au_fsctx_opts *a = fc->fs_private; + + sb = a->sb; + AuDebugOn(!sb); + + err = 0; + si_noflush_read_lock(sb); + bbot = au_sbbot(sb); + si_read_unlock(sb); + if (bindex <= bbot) + xino_itrunc->bindex = bindex; + else { + err = -EINVAL; + errorfc(fc, "out of bounds, %u", bindex); + } + + AuTraceErr(err); + return err; +} + +static int au_fsctx_parse_param(struct fs_context *fc, struct fs_parameter *param) +{ + int err, token; + struct fs_parse_result result; + struct au_fsctx_opts *a = fc->fs_private; + struct au_opt *opt = a->opt; + + AuDbg("fc %p, param {key %s, string %s}\n", + fc, param->key, param->string); + err = fs_parse(fc, aufs_fsctx_paramspec, param, &result); + if (unlikely(err < 0)) + goto out; + token = err; + AuDbg("token %d, res{negated %d, uint64 %llu}\n", + token, result.negated, result.uint_64); + + err = -EINVAL; + a->skipped = 0; + switch (token) { + case Opt_br: + err = au_fsctx_parse_br(fc, param->string); + break; + case Opt_add: + err = au_fsctx_parse_add(fc, param->string); + break; + case Opt_append: + err = au_fsctx_parse_do_add(fc, opt, param->string, param->size, + /*dummy bindex*/1); + break; + case Opt_prepend: + err = au_fsctx_parse_do_add(fc, opt, param->string, param->size, + /*bindex*/0); + break; + + case Opt_del: + err = au_fsctx_parse_del(fc, &opt->del, param); + break; +#if 0 /* reserved for future use */ + case Opt_idel: + if (!a->sb) { + err = 0; + a->skipped = 1; + break; + } + del->pathname = "(indexed)"; + err = au_opts_parse_idel(fc, &opt->del, result.uint_32); + break; +#endif + + case Opt_mod: + err = au_fsctx_parse_mod(fc, &opt->mod, param); + break; +#ifdef IMOD /* reserved for future use */ + case Opt_imod: + if (!a->sb) { + err = 0; + a->skipped = 1; + break; + } + u.mod->path = "(indexed)"; + err = au_opts_parse_imod(fc, &opt->mod, param->string); + break; +#endif + + case Opt_xino: + err = au_fsctx_parse_xino(fc, &opt->xino, param); + break; + case Opt_trunc_xino_path: + if (!a->sb) { + errorfc(fc, "no such branch %s", param->string); + break; + } + err = au_fsctx_parse_xino_itrunc_path(fc, &opt->xino_itrunc, + param->string); + break; +#if 0 + case Opt_trunc_xino_v: + if (!a->sb) { + err = 0; + a->skipped = 1; + break; + } + err = au_fsctx_parse_xino_itrunc_path(fc, &opt->xino_itrunc, + param->string); + break; +#endif + case Opt_itrunc_xino: + if (!a->sb) { + errorfc(fc, "out of bounds %s", param->string); + break; + } + err = au_fsctx_parse_xino_itrunc(fc, &opt->xino_itrunc, + result.int_32); + break; + + case Opt_dirwh: + err = 0; + opt->dirwh = result.int_32; + break; + + case Opt_rdcache: + if (unlikely(result.int_32 > AUFS_RDCACHE_MAX)) { + errorfc(fc, "rdcache must be smaller than %d", + AUFS_RDCACHE_MAX); + break; + } + err = 0; + opt->rdcache = result.int_32; + break; + + case Opt_rdblk: + err = 0; + opt->rdblk = AUFS_RDBLK_DEF; + if (!strcmp(param->string, "def")) + break; + + err = kstrtoint(param->string, 0, &result.int_32); + if (unlikely(err)) { + errorfc(fc, "bad value in %s", param->key); + break; + } + err = -EINVAL; + if (unlikely(result.int_32 < 0 + || result.int_32 > KMALLOC_MAX_SIZE)) { + errorfc(fc, "bad value in %s", param->key); + break; + } + if (unlikely(result.int_32 && result.int_32 < NAME_MAX)) { + errorfc(fc, "rdblk must be larger than %d", NAME_MAX); + break; + } + err = 0; + opt->rdblk = result.int_32; + break; + + case Opt_rdhash: + err = 0; + opt->rdhash = AUFS_RDHASH_DEF; + if (!strcmp(param->string, "def")) + break; + + err = kstrtoint(param->string, 0, &result.int_32); + if (unlikely(err)) { + errorfc(fc, "bad value in %s", param->key); + break; + } + /* how about zero? */ + if (result.int_32 < 0 + || result.int_32 * sizeof(struct hlist_head) + > KMALLOC_MAX_SIZE) { + err = -EINVAL; + errorfc(fc, "bad integer in %s", param->key); + break; + } + opt->rdhash = result.int_32; + break; + + case Opt_diropq: + /* + * As other options, fs/aufs/opts.c can handle these strings by + * match_token(). But "diropq=" is deprecated now and will + * never have other value. So simple strcmp() is enough here. + */ + if (!strcmp(param->string, "a") || + !strcmp(param->string, "always")) { + err = 0; + opt->type = Opt_diropq_a; + } else if (!strcmp(param->string, "w") || + !strcmp(param->string, "whiteouted")) { + err = 0; + opt->type = Opt_diropq_w; + } else + errorfc(fc, "unknown value %s", param->string); + break; + + case Opt_udba: + opt->udba = au_udba_val(param->string); + if (opt->udba >= 0) + err = 0; + else + errorf(fc, "wrong value, %s", param->string); + break; + + case Opt_wbr_create: + opt->wbr_create.wbr_create + = au_wbr_create_val(param->string, &opt->wbr_create); + if (opt->wbr_create.wbr_create >= 0) + err = 0; + else + errorf(fc, "wrong value, %s", param->key); + break; + + case Opt_wbr_copyup: + opt->wbr_copyup = au_wbr_copyup_val(param->string); + if (opt->wbr_copyup >= 0) + err = 0; + else + errorfc(fc, "wrong value, %s", param->key); + break; + + case Opt_fhsm_sec: + if (unlikely(result.int_32 < 0)) { + errorfc(fc, "bad integer in %s\n", param->key); + break; + } + err = 0; + if (sysaufs_brs) + opt->fhsm_second = result.int_32; + else + warnfc(fc, "ignored %s %s", param->key, param->string); + break; + + /* simple true/false flag */ +#define au_fsctx_TF(name) \ + case Opt_##name: \ + err = 0; \ + opt->tf = !result.negated; \ + break + au_fsctx_TF(trunc_xino); + au_fsctx_TF(trunc_xib); + au_fsctx_TF(dirperm1); + au_fsctx_TF(plink); + au_fsctx_TF(shwh); + au_fsctx_TF(dio); + au_fsctx_TF(warn_perm); + au_fsctx_TF(verbose); + au_fsctx_TF(sum); + au_fsctx_TF(dirren); + au_fsctx_TF(acl); +#undef au_fsctx_TF + + case Opt_noverbose: + err = 0; + opt->type = Opt_verbose; + opt->tf = false; + break; + + case Opt_noxino: + fallthrough; + case Opt_list_plink: + fallthrough; + case Opt_wsum: + err = 0; + break; + + case Opt_ignore: + warnfc(fc, "ignored %s", param->key); + fallthrough; + case Opt_ignore_silent: + a->skipped = 1; + err = 0; + break; + default: + a->skipped = 1; + err = -ENOPARAM; + break; + } + if (unlikely(err)) + goto out; + if (a->skipped) + goto out; + + switch (token) { + case Opt_br: + fallthrough; + case Opt_noverbose: + fallthrough; + case Opt_diropq: + break; + default: + opt->type = token; + break; + } + opt++; + if (unlikely(opt > a->opt_tail)) { + err = -E2BIG; + opt--; + } + opt->type = Opt_tail; + a->opt = opt; + +out: + return err; +} + +/* + * these options accept both 'name=val' and 'name:val' form. + * some accept optional '=' in its value. + * eg. br:/br1=rw:/br2=ro and br=/br1=rw:/br2=ro + */ +static inline unsigned int is_colonopt(char *str) +{ +#define do_test(name) \ + if (!strncmp(str, name ":", sizeof(name))) \ + return sizeof(name) - 1 + do_test("br"); + do_test("add"); + do_test("ins"); + do_test("append"); + do_test("prepend"); + do_test("del"); + /* do_test("idel"); */ + do_test("mod"); + /* do_test("imod"); */ +#undef do_test + + return 0; +} + +static int au_fsctx_parse_monolithic(struct fs_context *fc, void *data) +{ + int err; + unsigned int u; + char *str; + struct au_fsctx_opts *a = fc->fs_private; + + str = data; + AuDbg("str %s\n", str); + while (str) { + u = is_colonopt(str); + if (u) + str[u] = '='; + str = strchr(str, ','); + if (!str) + break; + str++; + } + str = data; + AuDbg("str %s\n", str); + + err = generic_parse_monolithic(fc, str); + AuTraceErr(err); + au_fsctx_dump(&a->opts); + + return err; +} + +/* ---------------------------------------------------------------------- */ + +static void au_fsctx_opts_free(struct au_opts *opts) +{ + struct au_opt *opt; + + opt = opts->opt; + while (opt->type != Opt_tail) { + switch (opt->type) { + case Opt_add: + fallthrough; + case Opt_append: + fallthrough; + case Opt_prepend: + kfree(opt->add.pathname); + path_put(&opt->add.path); + break; + case Opt_del: + kfree(opt->del.pathname); + fallthrough; + case Opt_idel: + path_put(&opt->del.h_path); + break; + case Opt_mod: + kfree(opt->mod.path); + fallthrough; + case Opt_imod: + dput(opt->mod.h_root); + break; + case Opt_xino: + kfree(opt->xino.path); + fput(opt->xino.file); + break; + } + opt++; + } +} + +static void au_fsctx_free(struct fs_context *fc) +{ + struct au_fsctx_opts *a = fc->fs_private; + + /* fs_type=%p, root=%pD */ + AuDbg("fc %p{sb_flags 0x%x, sb_flags_mask 0x%x, purpose %u\n", + fc, fc->sb_flags, fc->sb_flags_mask, fc->purpose); + + kobject_put(&a->sbinfo->si_kobj); + au_fsctx_opts_free(&a->opts); + free_page((unsigned long)a->opts.opt); + au_kfree_rcu(a); +} + +static const struct fs_context_operations au_fsctx_ops = { + .free = au_fsctx_free, + .parse_param = au_fsctx_parse_param, + .parse_monolithic = au_fsctx_parse_monolithic, + .get_tree = au_fsctx_get_tree, + .reconfigure = au_fsctx_reconfigure + /* + * nfs4 requires ->dup()? No. + * I don't know what is this ->dup() for. + */ +}; + +int aufs_fsctx_init(struct fs_context *fc) +{ + int err; + struct au_fsctx_opts *a; + + /* fs_type=%p, root=%pD */ + AuDbg("fc %p{sb_flags 0x%x, sb_flags_mask 0x%x, purpose %u\n", + fc, fc->sb_flags, fc->sb_flags_mask, fc->purpose); + + /* they will be freed by au_fsctx_free() */ + err = -ENOMEM; + a = kzalloc(sizeof(*a), GFP_NOFS); + if (unlikely(!a)) + goto out; + a->bindex = 0; + a->opts.opt = (void *)__get_free_page(GFP_NOFS); + if (unlikely(!a->opts.opt)) + goto out_a; + a->opt = a->opts.opt; + a->opt->type = Opt_tail; + a->opts.max_opt = PAGE_SIZE / sizeof(*a->opts.opt); + a->opt_tail = a->opt + a->opts.max_opt - 1; + a->opts.sb_flags = fc->sb_flags; + + a->sb = NULL; + if (fc->root) { + AuDebugOn(fc->purpose != FS_CONTEXT_FOR_RECONFIGURE); + a->opts.flags = AuOpts_REMOUNT; + a->sb = fc->root->d_sb; + a->sbinfo = au_sbi(a->sb); + kobject_get(&a->sbinfo->si_kobj); + } else { + a->sbinfo = au_si_alloc(a->sb); + AuDebugOn(!a->sbinfo); + err = PTR_ERR(a->sbinfo); + if (IS_ERR(a->sbinfo)) + goto out_opt; + au_rw_write_unlock(&a->sbinfo->si_rwsem); + } + + err = 0; + fc->fs_private = a; + fc->ops = &au_fsctx_ops; + goto out; /* success */ + +out_opt: + free_page((unsigned long)a->opts.opt); +out_a: + au_kfree_rcu(a); +out: + AuTraceErr(err); + return err; +} diff --git a/fs/aufs/fstype.h b/fs/aufs/fstype.h new file mode 100644 index 0000000000000..33e7f2dc8a956 --- /dev/null +++ b/fs/aufs/fstype.h @@ -0,0 +1,401 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * judging filesystem type + */ + +#ifndef __AUFS_FSTYPE_H__ +#define __AUFS_FSTYPE_H__ + +#ifdef __KERNEL__ + +#include +#include +#include +#include + +static inline int au_test_aufs(struct super_block *sb) +{ + return sb->s_magic == AUFS_SUPER_MAGIC; +} + +static inline const char *au_sbtype(struct super_block *sb) +{ + return sb->s_type->name; +} + +static inline int au_test_iso9660(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_ISO9660_FS) + return sb->s_magic == ISOFS_SUPER_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_romfs(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_ROMFS_FS) + return sb->s_magic == ROMFS_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_cramfs(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_CRAMFS) + return sb->s_magic == CRAMFS_MAGIC; +#endif + return 0; +} + +static inline int au_test_nfs(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_NFS_FS) + return sb->s_magic == NFS_SUPER_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_fuse(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_FUSE_FS) + return sb->s_magic == FUSE_SUPER_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_xfs(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_XFS_FS) + return sb->s_magic == XFS_SB_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_tmpfs(struct super_block *sb __maybe_unused) +{ +#ifdef CONFIG_TMPFS + return sb->s_magic == TMPFS_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_ecryptfs(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_ECRYPT_FS) + return !strcmp(au_sbtype(sb), "ecryptfs"); +#else + return 0; +#endif +} + +static inline int au_test_ramfs(struct super_block *sb) +{ + return sb->s_magic == RAMFS_MAGIC; +} + +static inline int au_test_ubifs(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_UBIFS_FS) + return sb->s_magic == UBIFS_SUPER_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_procfs(struct super_block *sb __maybe_unused) +{ +#ifdef CONFIG_PROC_FS + return sb->s_magic == PROC_SUPER_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_sysfs(struct super_block *sb __maybe_unused) +{ +#ifdef CONFIG_SYSFS + return sb->s_magic == SYSFS_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_configfs(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_CONFIGFS_FS) + return sb->s_magic == CONFIGFS_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_minix(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_MINIX_FS) + return sb->s_magic == MINIX3_SUPER_MAGIC + || sb->s_magic == MINIX2_SUPER_MAGIC + || sb->s_magic == MINIX2_SUPER_MAGIC2 + || sb->s_magic == MINIX_SUPER_MAGIC + || sb->s_magic == MINIX_SUPER_MAGIC2; +#else + return 0; +#endif +} + +static inline int au_test_fat(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_FAT_FS) + return sb->s_magic == MSDOS_SUPER_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_msdos(struct super_block *sb) +{ + return au_test_fat(sb); +} + +static inline int au_test_vfat(struct super_block *sb) +{ + return au_test_fat(sb); +} + +static inline int au_test_securityfs(struct super_block *sb __maybe_unused) +{ +#ifdef CONFIG_SECURITYFS + return sb->s_magic == SECURITYFS_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_squashfs(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_SQUASHFS) + return sb->s_magic == SQUASHFS_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_btrfs(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_BTRFS_FS) + return sb->s_magic == BTRFS_SUPER_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_xenfs(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_XENFS) + return sb->s_magic == XENFS_SUPER_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_debugfs(struct super_block *sb __maybe_unused) +{ +#ifdef CONFIG_DEBUG_FS + return sb->s_magic == DEBUGFS_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_nilfs(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_NILFS) + return sb->s_magic == NILFS_SUPER_MAGIC; +#else + return 0; +#endif +} + +static inline int au_test_hfsplus(struct super_block *sb __maybe_unused) +{ +#if IS_ENABLED(CONFIG_HFSPLUS_FS) + return sb->s_magic == HFSPLUS_SUPER_MAGIC; +#else + return 0; +#endif +} + +/* ---------------------------------------------------------------------- */ +/* + * they can't be an aufs branch. + */ +static inline int au_test_fs_unsuppoted(struct super_block *sb) +{ + return +#ifndef CONFIG_AUFS_BR_RAMFS + au_test_ramfs(sb) || +#endif + au_test_procfs(sb) + || au_test_sysfs(sb) + || au_test_configfs(sb) + || au_test_debugfs(sb) + || au_test_securityfs(sb) + || au_test_xenfs(sb) + || au_test_ecryptfs(sb) + /* || !strcmp(au_sbtype(sb), "unionfs") */ + || au_test_aufs(sb); /* will be supported in next version */ +} + +static inline int au_test_fs_remote(struct super_block *sb) +{ + return !au_test_tmpfs(sb) +#ifdef CONFIG_AUFS_BR_RAMFS + && !au_test_ramfs(sb) +#endif + && !(sb->s_type->fs_flags & FS_REQUIRES_DEV); +} + +/* ---------------------------------------------------------------------- */ + +/* + * Note: these functions (below) are created after reading ->getattr() in all + * filesystems under linux/fs. it means we have to do so in every update... + */ + +/* + * some filesystems require getattr to refresh the inode attributes before + * referencing. + * in most cases, we can rely on the inode attribute in NFS (or every remote fs) + * and leave the work for d_revalidate() + */ +static inline int au_test_fs_refresh_iattr(struct super_block *sb) +{ + return au_test_nfs(sb) + || au_test_fuse(sb) + /* || au_test_btrfs(sb) */ /* untested */ + ; +} + +/* + * filesystems which don't maintain i_size or i_blocks. + */ +static inline int au_test_fs_bad_iattr_size(struct super_block *sb) +{ + return au_test_xfs(sb) + || au_test_btrfs(sb) + || au_test_ubifs(sb) + || au_test_hfsplus(sb) /* maintained, but incorrect */ + /* || au_test_minix(sb) */ /* untested */ + ; +} + +/* + * filesystems which don't store the correct value in some of their inode + * attributes. + */ +static inline int au_test_fs_bad_iattr(struct super_block *sb) +{ + return au_test_fs_bad_iattr_size(sb) + || au_test_fat(sb) + || au_test_msdos(sb) + || au_test_vfat(sb); +} + +/* they don't check i_nlink in link(2) */ +static inline int au_test_fs_no_limit_nlink(struct super_block *sb) +{ + return au_test_tmpfs(sb) +#ifdef CONFIG_AUFS_BR_RAMFS + || au_test_ramfs(sb) +#endif + || au_test_ubifs(sb) + || au_test_hfsplus(sb); +} + +/* + * filesystems which sets S_NOATIME and S_NOCMTIME. + */ +static inline int au_test_fs_notime(struct super_block *sb) +{ + return au_test_nfs(sb) + || au_test_fuse(sb) + || au_test_ubifs(sb) + ; +} + +/* temporary support for i#1 in cramfs */ +static inline int au_test_fs_unique_ino(struct inode *inode) +{ + if (au_test_cramfs(inode->i_sb)) + return inode->i_ino != 1; + return 1; +} + +/* ---------------------------------------------------------------------- */ + +/* + * the filesystem where the xino files placed must support i/o after unlink and + * maintain i_size and i_blocks. + */ +static inline int au_test_fs_bad_xino(struct super_block *sb) +{ + return au_test_fs_remote(sb) + || au_test_fs_bad_iattr_size(sb) + /* don't want unnecessary work for xino */ + || au_test_aufs(sb) + || au_test_ecryptfs(sb) + || au_test_nilfs(sb); +} + +static inline int au_test_fs_trunc_xino(struct super_block *sb) +{ + return au_test_tmpfs(sb) + || au_test_ramfs(sb); +} + +/* + * test if the @sb is real-readonly. + */ +static inline int au_test_fs_rr(struct super_block *sb) +{ + return au_test_squashfs(sb) + || au_test_iso9660(sb) + || au_test_cramfs(sb) + || au_test_romfs(sb); +} + +/* + * test if the @inode is nfs with 'noacl' option + * NFS always sets SB_POSIXACL regardless its mount option 'noacl.' + */ +static inline int au_test_nfs_noacl(struct inode *inode) +{ + return au_test_nfs(inode->i_sb) + /* && IS_POSIXACL(inode) */ + && !nfs_server_capable(inode, NFS_CAP_ACLS); +} + +#endif /* __KERNEL__ */ +#endif /* __AUFS_FSTYPE_H__ */ diff --git a/fs/aufs/hbl.h b/fs/aufs/hbl.h new file mode 100644 index 0000000000000..33b6f7da81eb9 --- /dev/null +++ b/fs/aufs/hbl.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2017-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * helpers for hlist_bl.h + */ + +#ifndef __AUFS_HBL_H__ +#define __AUFS_HBL_H__ + +#ifdef __KERNEL__ + +#include + +static inline void au_hbl_add(struct hlist_bl_node *node, + struct hlist_bl_head *hbl) +{ + hlist_bl_lock(hbl); + hlist_bl_add_head(node, hbl); + hlist_bl_unlock(hbl); +} + +static inline void au_hbl_del(struct hlist_bl_node *node, + struct hlist_bl_head *hbl) +{ + hlist_bl_lock(hbl); + hlist_bl_del(node); + hlist_bl_unlock(hbl); +} + +#define au_hbl_for_each(pos, head) \ + for (pos = hlist_bl_first(head); \ + pos; \ + pos = pos->next) + +static inline unsigned long au_hbl_count(struct hlist_bl_head *hbl) +{ + unsigned long cnt; + struct hlist_bl_node *pos; + + cnt = 0; + hlist_bl_lock(hbl); + au_hbl_for_each(pos, hbl) + cnt++; + hlist_bl_unlock(hbl); + return cnt; +} + +#endif /* __KERNEL__ */ +#endif /* __AUFS_HBL_H__ */ diff --git a/fs/aufs/hfsnotify.c b/fs/aufs/hfsnotify.c new file mode 100644 index 0000000000000..b029fa2085a82 --- /dev/null +++ b/fs/aufs/hfsnotify.c @@ -0,0 +1,288 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * fsnotify for the lower directories + */ + +#include "aufs.h" + +/* FS_IN_IGNORED is unnecessary */ +static const __u32 AuHfsnMask = (FS_MOVED_TO | FS_MOVED_FROM | FS_DELETE + | FS_CREATE | FS_EVENT_ON_CHILD); +static DECLARE_WAIT_QUEUE_HEAD(au_hfsn_wq); +static __cacheline_aligned_in_smp atomic64_t au_hfsn_ifree = ATOMIC64_INIT(0); + +static void au_hfsn_free_mark(struct fsnotify_mark *mark) +{ + struct au_hnotify *hn = container_of(mark, struct au_hnotify, + hn_mark); + /* AuDbg("here\n"); */ + au_cache_free_hnotify(hn); + smp_mb__before_atomic(); /* for atomic64_dec */ + if (atomic64_dec_and_test(&au_hfsn_ifree)) + wake_up(&au_hfsn_wq); +} + +static int au_hfsn_alloc(struct au_hinode *hinode) +{ + int err; + struct au_hnotify *hn; + struct super_block *sb; + struct au_branch *br; + struct fsnotify_mark *mark; + aufs_bindex_t bindex; + + hn = hinode->hi_notify; + sb = hn->hn_aufs_inode->i_sb; + bindex = au_br_index(sb, hinode->hi_id); + br = au_sbr(sb, bindex); + AuDebugOn(!br->br_hfsn); + + mark = &hn->hn_mark; + fsnotify_init_mark(mark, br->br_hfsn->hfsn_group); + mark->mask = AuHfsnMask; + /* + * by udba rename or rmdir, aufs assign a new inode to the known + * h_inode, so specify 1 to allow dups. + */ + lockdep_off(); + err = fsnotify_add_inode_mark(mark, hinode->hi_inode, /*allow_dups*/1); + lockdep_on(); + + return err; +} + +static int au_hfsn_free(struct au_hinode *hinode, struct au_hnotify *hn) +{ + struct fsnotify_mark *mark; + unsigned long long ull; + struct fsnotify_group *group; + + ull = atomic64_inc_return(&au_hfsn_ifree); + BUG_ON(!ull); + + mark = &hn->hn_mark; + spin_lock(&mark->lock); + group = mark->group; + fsnotify_get_group(group); + spin_unlock(&mark->lock); + lockdep_off(); + fsnotify_destroy_mark(mark, group); + fsnotify_put_mark(mark); + fsnotify_put_group(group); + lockdep_on(); + + /* free hn by myself */ + return 0; +} + +/* ---------------------------------------------------------------------- */ + +static void au_hfsn_ctl(struct au_hinode *hinode, int do_set) +{ + struct fsnotify_mark *mark; + + mark = &hinode->hi_notify->hn_mark; + spin_lock(&mark->lock); + if (do_set) { + AuDebugOn(mark->mask & AuHfsnMask); + mark->mask |= AuHfsnMask; + } else { + AuDebugOn(!(mark->mask & AuHfsnMask)); + mark->mask &= ~AuHfsnMask; + } + spin_unlock(&mark->lock); + /* fsnotify_recalc_inode_mask(hinode->hi_inode); */ +} + +/* ---------------------------------------------------------------------- */ + +/* #define AuDbgHnotify */ +#ifdef AuDbgHnotify +static char *au_hfsn_name(u32 mask) +{ +#ifdef CONFIG_AUFS_DEBUG +#define test_ret(flag) \ + do { \ + if (mask & flag) \ + return #flag; \ + } while (0) + test_ret(FS_ACCESS); + test_ret(FS_MODIFY); + test_ret(FS_ATTRIB); + test_ret(FS_CLOSE_WRITE); + test_ret(FS_CLOSE_NOWRITE); + test_ret(FS_OPEN); + test_ret(FS_MOVED_FROM); + test_ret(FS_MOVED_TO); + test_ret(FS_CREATE); + test_ret(FS_DELETE); + test_ret(FS_DELETE_SELF); + test_ret(FS_MOVE_SELF); + test_ret(FS_UNMOUNT); + test_ret(FS_Q_OVERFLOW); + test_ret(FS_IN_IGNORED); + test_ret(FS_ISDIR); + test_ret(FS_IN_ONESHOT); + test_ret(FS_EVENT_ON_CHILD); + return ""; +#undef test_ret +#else + return "??"; +#endif +} +#endif + +/* ---------------------------------------------------------------------- */ + +static void au_hfsn_free_group(struct fsnotify_group *group) +{ + struct au_br_hfsnotify *hfsn = group->private; + + /* AuDbg("here\n"); */ + au_kfree_try_rcu(hfsn); +} + +static int au_hfsn_handle_event(struct fsnotify_group *group, + u32 mask, const void *data, int data_type, + struct inode *dir, + const struct qstr *file_name, u32 cookie, + struct fsnotify_iter_info *iter_info) +{ + int err; + struct au_hnotify *hnotify; + struct inode *h_dir, *h_inode; + struct fsnotify_mark *inode_mark; + + AuDebugOn(data_type != FSNOTIFY_EVENT_INODE); + + err = 0; + /* if FS_UNMOUNT happens, there must be another bug */ + AuDebugOn(mask & FS_UNMOUNT); + if (mask & (FS_IN_IGNORED | FS_UNMOUNT)) + goto out; + + h_dir = dir; + h_inode = NULL; +#ifdef AuDbgHnotify + au_debug_on(); + if (1 || h_child_qstr.len != sizeof(AUFS_XINO_FNAME) - 1 + || strncmp(h_child_qstr.name, AUFS_XINO_FNAME, h_child_qstr.len)) { + AuDbg("i%lu, mask 0x%x %s, hcname %.*s, hi%lu\n", + h_dir->i_ino, mask, au_hfsn_name(mask), + AuLNPair(&h_child_qstr), h_inode ? h_inode->i_ino : 0); + /* WARN_ON(1); */ + } + au_debug_off(); +#endif + + inode_mark = fsnotify_iter_inode_mark(iter_info); + AuDebugOn(!inode_mark); + hnotify = container_of(inode_mark, struct au_hnotify, hn_mark); + err = au_hnotify(h_dir, hnotify, mask, file_name, h_inode); + +out: + return err; +} + +static struct fsnotify_ops au_hfsn_ops = { + .handle_event = au_hfsn_handle_event, + .free_group_priv = au_hfsn_free_group, + .free_mark = au_hfsn_free_mark +}; + +/* ---------------------------------------------------------------------- */ + +static void au_hfsn_fin_br(struct au_branch *br) +{ + struct au_br_hfsnotify *hfsn; + + hfsn = br->br_hfsn; + if (hfsn) { + lockdep_off(); + fsnotify_put_group(hfsn->hfsn_group); + lockdep_on(); + } +} + +static int au_hfsn_init_br(struct au_branch *br, int perm) +{ + int err; + struct fsnotify_group *group; + struct au_br_hfsnotify *hfsn; + + err = 0; + br->br_hfsn = NULL; + if (!au_br_hnotifyable(perm)) + goto out; + + err = -ENOMEM; + hfsn = kmalloc(sizeof(*hfsn), GFP_NOFS); + if (unlikely(!hfsn)) + goto out; + + err = 0; + group = fsnotify_alloc_group(&au_hfsn_ops); + if (IS_ERR(group)) { + err = PTR_ERR(group); + pr_err("fsnotify_alloc_group() failed, %d\n", err); + goto out_hfsn; + } + + group->private = hfsn; + hfsn->hfsn_group = group; + br->br_hfsn = hfsn; + goto out; /* success */ + +out_hfsn: + au_kfree_try_rcu(hfsn); +out: + return err; +} + +static int au_hfsn_reset_br(unsigned int udba, struct au_branch *br, int perm) +{ + int err; + + err = 0; + if (!br->br_hfsn) + err = au_hfsn_init_br(br, perm); + + return err; +} + +/* ---------------------------------------------------------------------- */ + +static void au_hfsn_fin(void) +{ + AuDbg("au_hfsn_ifree %lld\n", (long long)atomic64_read(&au_hfsn_ifree)); + wait_event(au_hfsn_wq, !atomic64_read(&au_hfsn_ifree)); +} + +const struct au_hnotify_op au_hnotify_op = { + .ctl = au_hfsn_ctl, + .alloc = au_hfsn_alloc, + .free = au_hfsn_free, + + .fin = au_hfsn_fin, + + .reset_br = au_hfsn_reset_br, + .fin_br = au_hfsn_fin_br, + .init_br = au_hfsn_init_br +}; diff --git a/fs/aufs/hfsplus.c b/fs/aufs/hfsplus.c new file mode 100644 index 0000000000000..e65d87b3fcc13 --- /dev/null +++ b/fs/aufs/hfsplus.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2010-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * special support for filesystems which acquires an inode mutex + * at final closing a file, eg, hfsplus. + * + * This trick is very simple and stupid, just to open the file before really + * necessary open to tell hfsplus that this is not the final closing. + * The caller should call au_h_open_pre() after acquiring the inode mutex, + * and au_h_open_post() after releasing it. + */ + +#include "aufs.h" + +struct file *au_h_open_pre(struct dentry *dentry, aufs_bindex_t bindex, + int force_wr) +{ + struct file *h_file; + struct dentry *h_dentry; + + h_dentry = au_h_dptr(dentry, bindex); + AuDebugOn(!h_dentry); + AuDebugOn(d_is_negative(h_dentry)); + + h_file = NULL; + if (au_test_hfsplus(h_dentry->d_sb) + && d_is_reg(h_dentry)) + h_file = au_h_open(dentry, bindex, + O_RDONLY | O_NOATIME | O_LARGEFILE, + /*file*/NULL, force_wr); + return h_file; +} + +void au_h_open_post(struct dentry *dentry, aufs_bindex_t bindex, + struct file *h_file) +{ + struct au_branch *br; + + if (h_file) { + fput(h_file); + br = au_sbr(dentry->d_sb, bindex); + au_lcnt_dec(&br->br_nfiles); + } +} diff --git a/fs/aufs/hnotify.c b/fs/aufs/hnotify.c new file mode 100644 index 0000000000000..5c4d95bf33c27 --- /dev/null +++ b/fs/aufs/hnotify.c @@ -0,0 +1,715 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * abstraction to notify the direct changes on lower directories + */ + +/* #include */ +#include "aufs.h" + +int au_hn_alloc(struct au_hinode *hinode, struct inode *inode) +{ + int err; + struct au_hnotify *hn; + + err = -ENOMEM; + hn = au_cache_alloc_hnotify(); + if (hn) { + hn->hn_aufs_inode = inode; + hinode->hi_notify = hn; + err = au_hnotify_op.alloc(hinode); + AuTraceErr(err); + if (unlikely(err)) { + hinode->hi_notify = NULL; + au_cache_free_hnotify(hn); + /* + * The upper dir was removed by udba, but the same named + * dir left. In this case, aufs assigns a new inode + * number and set the monitor again. + * For the lower dir, the old monitor is still left. + */ + if (err == -EEXIST) + err = 0; + } + } + + AuTraceErr(err); + return err; +} + +void au_hn_free(struct au_hinode *hinode) +{ + struct au_hnotify *hn; + + hn = hinode->hi_notify; + if (hn) { + hinode->hi_notify = NULL; + if (au_hnotify_op.free(hinode, hn)) + au_cache_free_hnotify(hn); + } +} + +/* ---------------------------------------------------------------------- */ + +void au_hn_ctl(struct au_hinode *hinode, int do_set) +{ + if (hinode->hi_notify) + au_hnotify_op.ctl(hinode, do_set); +} + +void au_hn_reset(struct inode *inode, unsigned int flags) +{ + aufs_bindex_t bindex, bbot; + struct inode *hi; + struct dentry *iwhdentry; + + bbot = au_ibbot(inode); + for (bindex = au_ibtop(inode); bindex <= bbot; bindex++) { + hi = au_h_iptr(inode, bindex); + if (!hi) + continue; + + /* inode_lock_nested(hi, AuLsc_I_CHILD); */ + iwhdentry = au_hi_wh(inode, bindex); + if (iwhdentry) + dget(iwhdentry); + au_igrab(hi); + au_set_h_iptr(inode, bindex, NULL, 0); + au_set_h_iptr(inode, bindex, au_igrab(hi), + flags & ~AuHi_XINO); + iput(hi); + dput(iwhdentry); + /* inode_unlock(hi); */ + } +} + +/* ---------------------------------------------------------------------- */ + +static int hn_xino(struct inode *inode, struct inode *h_inode) +{ + int err; + aufs_bindex_t bindex, bbot, bfound, btop; + struct inode *h_i; + + err = 0; + if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { + pr_warn("branch root dir was changed\n"); + goto out; + } + + bfound = -1; + bbot = au_ibbot(inode); + btop = au_ibtop(inode); +#if 0 /* reserved for future use */ + if (bindex == bbot) { + /* keep this ino in rename case */ + goto out; + } +#endif + for (bindex = btop; bindex <= bbot; bindex++) + if (au_h_iptr(inode, bindex) == h_inode) { + bfound = bindex; + break; + } + if (bfound < 0) + goto out; + + for (bindex = btop; bindex <= bbot; bindex++) { + h_i = au_h_iptr(inode, bindex); + if (!h_i) + continue; + + err = au_xino_write(inode->i_sb, bindex, h_i->i_ino, /*ino*/0); + /* ignore this error */ + /* bad action? */ + } + + /* children inode number will be broken */ + +out: + AuTraceErr(err); + return err; +} + +static int hn_gen_tree(struct dentry *dentry) +{ + int err, i, j, ndentry; + struct au_dcsub_pages dpages; + struct au_dpage *dpage; + struct dentry **dentries; + + err = au_dpages_init(&dpages, GFP_NOFS); + if (unlikely(err)) + goto out; + err = au_dcsub_pages(&dpages, dentry, NULL, NULL); + if (unlikely(err)) + goto out_dpages; + + for (i = 0; i < dpages.ndpage; i++) { + dpage = dpages.dpages + i; + dentries = dpage->dentries; + ndentry = dpage->ndentry; + for (j = 0; j < ndentry; j++) { + struct dentry *d; + + d = dentries[j]; + if (IS_ROOT(d)) + continue; + + au_digen_dec(d); + if (d_really_is_positive(d)) + /* todo: reset children xino? + cached children only? */ + au_iigen_dec(d_inode(d)); + } + } + +out_dpages: + au_dpages_free(&dpages); +out: + return err; +} + +/* + * return 0 if processed. + */ +static int hn_gen_by_inode(char *name, unsigned int nlen, struct inode *inode, + const unsigned int isdir) +{ + int err; + struct dentry *d; + struct qstr *dname; + + err = 1; + if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { + pr_warn("branch root dir was changed\n"); + err = 0; + goto out; + } + + if (!isdir) { + AuDebugOn(!name); + au_iigen_dec(inode); + spin_lock(&inode->i_lock); + hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) { + spin_lock(&d->d_lock); + dname = &d->d_name; + if (dname->len != nlen + && memcmp(dname->name, name, nlen)) { + spin_unlock(&d->d_lock); + continue; + } + err = 0; + au_digen_dec(d); + spin_unlock(&d->d_lock); + break; + } + spin_unlock(&inode->i_lock); + } else { + au_fset_si(au_sbi(inode->i_sb), FAILED_REFRESH_DIR); + d = d_find_any_alias(inode); + if (!d) { + au_iigen_dec(inode); + goto out; + } + + spin_lock(&d->d_lock); + dname = &d->d_name; + if (dname->len == nlen && !memcmp(dname->name, name, nlen)) { + spin_unlock(&d->d_lock); + err = hn_gen_tree(d); + spin_lock(&d->d_lock); + } + spin_unlock(&d->d_lock); + dput(d); + } + +out: + AuTraceErr(err); + return err; +} + +static int hn_gen_by_name(struct dentry *dentry, const unsigned int isdir) +{ + int err; + + if (IS_ROOT(dentry)) { + pr_warn("branch root dir was changed\n"); + return 0; + } + + err = 0; + if (!isdir) { + au_digen_dec(dentry); + if (d_really_is_positive(dentry)) + au_iigen_dec(d_inode(dentry)); + } else { + au_fset_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIR); + if (d_really_is_positive(dentry)) + err = hn_gen_tree(dentry); + } + + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* hnotify job flags */ +#define AuHnJob_XINO0 1 +#define AuHnJob_GEN (1 << 1) +#define AuHnJob_DIRENT (1 << 2) +#define AuHnJob_ISDIR (1 << 3) +#define AuHnJob_TRYXINO0 (1 << 4) +#define AuHnJob_MNTPNT (1 << 5) +#define au_ftest_hnjob(flags, name) ((flags) & AuHnJob_##name) +#define au_fset_hnjob(flags, name) \ + do { (flags) |= AuHnJob_##name; } while (0) +#define au_fclr_hnjob(flags, name) \ + do { (flags) &= ~AuHnJob_##name; } while (0) + +enum { + AuHn_CHILD, + AuHn_PARENT, + AuHnLast +}; + +struct au_hnotify_args { + struct inode *h_dir, *dir, *h_child_inode; + u32 mask; + unsigned int flags[AuHnLast]; + unsigned int h_child_nlen; + char h_child_name[]; +}; + +struct hn_job_args { + unsigned int flags; + struct inode *inode, *h_inode, *dir, *h_dir; + struct dentry *dentry; + char *h_name; + int h_nlen; +}; + +static int hn_job(struct hn_job_args *a) +{ + const unsigned int isdir = au_ftest_hnjob(a->flags, ISDIR); + int e; + + /* reset xino */ + if (au_ftest_hnjob(a->flags, XINO0) && a->inode) + hn_xino(a->inode, a->h_inode); /* ignore this error */ + + if (au_ftest_hnjob(a->flags, TRYXINO0) + && a->inode + && a->h_inode) { + inode_lock_shared_nested(a->h_inode, AuLsc_I_CHILD); + if (!a->h_inode->i_nlink + && !(a->h_inode->i_state & I_LINKABLE)) + hn_xino(a->inode, a->h_inode); /* ignore this error */ + inode_unlock_shared(a->h_inode); + } + + /* make the generation obsolete */ + if (au_ftest_hnjob(a->flags, GEN)) { + e = -1; + if (a->inode) + e = hn_gen_by_inode(a->h_name, a->h_nlen, a->inode, + isdir); + if (e && a->dentry) + hn_gen_by_name(a->dentry, isdir); + /* ignore this error */ + } + + /* make dir entries obsolete */ + if (au_ftest_hnjob(a->flags, DIRENT) && a->inode) { + struct au_vdir *vdir; + + vdir = au_ivdir(a->inode); + if (vdir) + vdir->vd_jiffy = 0; + /* IMustLock(a->inode); */ + /* inode_inc_iversion(a->inode); */ + } + + /* can do nothing but warn */ + if (au_ftest_hnjob(a->flags, MNTPNT) + && a->dentry + && d_mountpoint(a->dentry)) + pr_warn("mount-point %pd is removed or renamed\n", a->dentry); + + return 0; +} + +/* ---------------------------------------------------------------------- */ + +static struct dentry *lookup_wlock_by_name(char *name, unsigned int nlen, + struct inode *dir) +{ + struct dentry *dentry, *d, *parent; + struct qstr *dname; + + parent = d_find_any_alias(dir); + if (!parent) + return NULL; + + dentry = NULL; + spin_lock(&parent->d_lock); + list_for_each_entry(d, &parent->d_subdirs, d_child) { + /* AuDbg("%pd\n", d); */ + spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED); + dname = &d->d_name; + if (dname->len != nlen || memcmp(dname->name, name, nlen)) + goto cont_unlock; + if (au_di(d)) + au_digen_dec(d); + else + goto cont_unlock; + if (au_dcount(d) > 0) { + dentry = dget_dlock(d); + spin_unlock(&d->d_lock); + break; + } + +cont_unlock: + spin_unlock(&d->d_lock); + } + spin_unlock(&parent->d_lock); + dput(parent); + + if (dentry) + di_write_lock_child(dentry); + + return dentry; +} + +static struct inode *lookup_wlock_by_ino(struct super_block *sb, + aufs_bindex_t bindex, ino_t h_ino) +{ + struct inode *inode; + ino_t ino; + int err; + + inode = NULL; + err = au_xino_read(sb, bindex, h_ino, &ino); + if (!err && ino) + inode = ilookup(sb, ino); + if (!inode) + goto out; + + if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { + pr_warn("wrong root branch\n"); + iput(inode); + inode = NULL; + goto out; + } + + ii_write_lock_child(inode); + +out: + return inode; +} + +static void au_hn_bh(void *_args) +{ + struct au_hnotify_args *a = _args; + struct super_block *sb; + aufs_bindex_t bindex, bbot, bfound; + unsigned char xino, try_iput; + int err; + struct inode *inode; + ino_t h_ino; + struct hn_job_args args; + struct dentry *dentry; + struct au_sbinfo *sbinfo; + + AuDebugOn(!_args); + AuDebugOn(!a->h_dir); + AuDebugOn(!a->dir); + AuDebugOn(!a->mask); + AuDbg("mask 0x%x, i%lu, hi%lu, hci%lu\n", + a->mask, a->dir->i_ino, a->h_dir->i_ino, + a->h_child_inode ? a->h_child_inode->i_ino : 0); + + inode = NULL; + dentry = NULL; + /* + * do not lock a->dir->i_mutex here + * because of d_revalidate() may cause a deadlock. + */ + sb = a->dir->i_sb; + AuDebugOn(!sb); + sbinfo = au_sbi(sb); + AuDebugOn(!sbinfo); + si_write_lock(sb, AuLock_NOPLMW); + + if (au_opt_test(sbinfo->si_mntflags, DIRREN)) + switch (a->mask & FS_EVENTS_POSS_ON_CHILD) { + case FS_MOVED_FROM: + case FS_MOVED_TO: + AuWarn1("DIRREN with UDBA may not work correctly " + "for the direct rename(2)\n"); + } + + ii_read_lock_parent(a->dir); + bfound = -1; + bbot = au_ibbot(a->dir); + for (bindex = au_ibtop(a->dir); bindex <= bbot; bindex++) + if (au_h_iptr(a->dir, bindex) == a->h_dir) { + bfound = bindex; + break; + } + ii_read_unlock(a->dir); + if (unlikely(bfound < 0)) + goto out; + + xino = !!au_opt_test(au_mntflags(sb), XINO); + h_ino = 0; + if (a->h_child_inode) + h_ino = a->h_child_inode->i_ino; + + if (a->h_child_nlen + && (au_ftest_hnjob(a->flags[AuHn_CHILD], GEN) + || au_ftest_hnjob(a->flags[AuHn_CHILD], MNTPNT))) + dentry = lookup_wlock_by_name(a->h_child_name, a->h_child_nlen, + a->dir); + try_iput = 0; + if (dentry && d_really_is_positive(dentry)) + inode = d_inode(dentry); + if (xino && !inode && h_ino + && (au_ftest_hnjob(a->flags[AuHn_CHILD], XINO0) + || au_ftest_hnjob(a->flags[AuHn_CHILD], TRYXINO0) + || au_ftest_hnjob(a->flags[AuHn_CHILD], GEN))) { + inode = lookup_wlock_by_ino(sb, bfound, h_ino); + try_iput = 1; + } + + args.flags = a->flags[AuHn_CHILD]; + args.dentry = dentry; + args.inode = inode; + args.h_inode = a->h_child_inode; + args.dir = a->dir; + args.h_dir = a->h_dir; + args.h_name = a->h_child_name; + args.h_nlen = a->h_child_nlen; + err = hn_job(&args); + if (dentry) { + if (au_di(dentry)) + di_write_unlock(dentry); + dput(dentry); + } + if (inode && try_iput) { + ii_write_unlock(inode); + iput(inode); + } + + ii_write_lock_parent(a->dir); + args.flags = a->flags[AuHn_PARENT]; + args.dentry = NULL; + args.inode = a->dir; + args.h_inode = a->h_dir; + args.dir = NULL; + args.h_dir = NULL; + args.h_name = NULL; + args.h_nlen = 0; + err = hn_job(&args); + ii_write_unlock(a->dir); + +out: + iput(a->h_child_inode); + iput(a->h_dir); + iput(a->dir); + si_write_unlock(sb); + au_nwt_done(&sbinfo->si_nowait); + au_kfree_rcu(a); +} + +/* ---------------------------------------------------------------------- */ + +int au_hnotify(struct inode *h_dir, struct au_hnotify *hnotify, u32 mask, + const struct qstr *h_child_qstr, struct inode *h_child_inode) +{ + int err, len; + unsigned int flags[AuHnLast], f; + unsigned char isdir, isroot, wh; + struct inode *dir; + struct au_hnotify_args *args; + char *p, *h_child_name; + + err = 0; + AuDebugOn(!hnotify || !hnotify->hn_aufs_inode); + dir = igrab(hnotify->hn_aufs_inode); + if (!dir) + goto out; + + isroot = (dir->i_ino == AUFS_ROOT_INO); + wh = 0; + h_child_name = (void *)h_child_qstr->name; + len = h_child_qstr->len; + if (h_child_name) { + if (len > AUFS_WH_PFX_LEN + && !memcmp(h_child_name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { + h_child_name += AUFS_WH_PFX_LEN; + len -= AUFS_WH_PFX_LEN; + wh = 1; + } + } + + isdir = 0; + if (h_child_inode) + isdir = !!S_ISDIR(h_child_inode->i_mode); + flags[AuHn_PARENT] = AuHnJob_ISDIR; + flags[AuHn_CHILD] = 0; + if (isdir) + flags[AuHn_CHILD] = AuHnJob_ISDIR; + au_fset_hnjob(flags[AuHn_PARENT], DIRENT); + au_fset_hnjob(flags[AuHn_CHILD], GEN); + switch (mask & ALL_FSNOTIFY_DIRENT_EVENTS) { + case FS_MOVED_FROM: + case FS_MOVED_TO: + au_fset_hnjob(flags[AuHn_CHILD], XINO0); + au_fset_hnjob(flags[AuHn_CHILD], MNTPNT); + fallthrough; + case FS_CREATE: + AuDebugOn(!h_child_name); + break; + + case FS_DELETE: + /* + * aufs never be able to get this child inode. + * revalidation should be in d_revalidate() + * by checking i_nlink, i_generation or d_unhashed(). + */ + AuDebugOn(!h_child_name); + au_fset_hnjob(flags[AuHn_CHILD], TRYXINO0); + au_fset_hnjob(flags[AuHn_CHILD], MNTPNT); + break; + + default: + AuDebugOn(1); + } + + if (wh) + h_child_inode = NULL; + + err = -ENOMEM; + /* iput() and kfree() will be called in au_hnotify() */ + args = kmalloc(sizeof(*args) + len + 1, GFP_NOFS); + if (unlikely(!args)) { + AuErr1("no memory\n"); + iput(dir); + goto out; + } + args->flags[AuHn_PARENT] = flags[AuHn_PARENT]; + args->flags[AuHn_CHILD] = flags[AuHn_CHILD]; + args->mask = mask; + args->dir = dir; + args->h_dir = igrab(h_dir); + if (h_child_inode) + h_child_inode = igrab(h_child_inode); /* can be NULL */ + args->h_child_inode = h_child_inode; + args->h_child_nlen = len; + if (len) { + p = (void *)args; + p += sizeof(*args); + memcpy(p, h_child_name, len); + p[len] = 0; + } + + /* NFS fires the event for silly-renamed one from kworker */ + f = 0; + if (!dir->i_nlink + || (au_test_nfs(h_dir->i_sb) && (mask & FS_DELETE))) + f = AuWkq_NEST; + err = au_wkq_nowait(au_hn_bh, args, dir->i_sb, f); + if (unlikely(err)) { + pr_err("wkq %d\n", err); + iput(args->h_child_inode); + iput(args->h_dir); + iput(args->dir); + au_kfree_rcu(args); + } + +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +int au_hnotify_reset_br(unsigned int udba, struct au_branch *br, int perm) +{ + int err; + + AuDebugOn(!(udba & AuOptMask_UDBA)); + + err = 0; + if (au_hnotify_op.reset_br) + err = au_hnotify_op.reset_br(udba, br, perm); + + return err; +} + +int au_hnotify_init_br(struct au_branch *br, int perm) +{ + int err; + + err = 0; + if (au_hnotify_op.init_br) + err = au_hnotify_op.init_br(br, perm); + + return err; +} + +void au_hnotify_fin_br(struct au_branch *br) +{ + if (au_hnotify_op.fin_br) + au_hnotify_op.fin_br(br); +} + +static void au_hn_destroy_cache(void) +{ + kmem_cache_destroy(au_cache[AuCache_HNOTIFY]); + au_cache[AuCache_HNOTIFY] = NULL; +} + +int __init au_hnotify_init(void) +{ + int err; + + err = -ENOMEM; + au_cache[AuCache_HNOTIFY] = AuCache(au_hnotify); + if (au_cache[AuCache_HNOTIFY]) { + err = 0; + if (au_hnotify_op.init) + err = au_hnotify_op.init(); + if (unlikely(err)) + au_hn_destroy_cache(); + } + AuTraceErr(err); + return err; +} + +void au_hnotify_fin(void) +{ + if (au_hnotify_op.fin) + au_hnotify_op.fin(); + + /* cf. au_cache_fin() */ + if (au_cache[AuCache_HNOTIFY]) + au_hn_destroy_cache(); +} diff --git a/fs/aufs/i_op.c b/fs/aufs/i_op.c new file mode 100644 index 0000000000000..d778b760c008d --- /dev/null +++ b/fs/aufs/i_op.c @@ -0,0 +1,1501 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * inode operations (except add/del/rename) + */ + +#include +#include +#include +#include +#include "aufs.h" + +static int h_permission(struct inode *h_inode, int mask, + struct path *h_path, int brperm) +{ + int err; + const unsigned char write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); + + err = -EPERM; + if (write_mask && IS_IMMUTABLE(h_inode)) + goto out; + + err = -EACCES; + if (((mask & MAY_EXEC) + && S_ISREG(h_inode->i_mode) + && (path_noexec(h_path) + || !(h_inode->i_mode & 0111)))) + goto out; + + /* + * - skip the lower fs test in the case of write to ro branch. + * - nfs dir permission write check is optimized, but a policy for + * link/rename requires a real check. + * - nfs always sets SB_POSIXACL regardless its mount option 'noacl.' + * in this case, generic_permission() returns -EOPNOTSUPP. + */ + if ((write_mask && !au_br_writable(brperm)) + || (au_test_nfs(h_inode->i_sb) && S_ISDIR(h_inode->i_mode) + && write_mask && !(mask & MAY_READ)) + || !h_inode->i_op->permission) { + /* AuLabel(generic_permission); */ + /* AuDbg("get_acl %ps\n", h_inode->i_op->get_acl); */ + err = generic_permission(h_inode, mask); + if (err == -EOPNOTSUPP && au_test_nfs_noacl(h_inode)) + err = h_inode->i_op->permission(h_inode, mask); + AuTraceErr(err); + } else { + /* AuLabel(h_inode->permission); */ + err = h_inode->i_op->permission(h_inode, mask); + AuTraceErr(err); + } + + if (!err) + err = devcgroup_inode_permission(h_inode, mask); + if (!err) + err = security_inode_permission(h_inode, mask); + +out: + return err; +} + +static int aufs_permission(struct inode *inode, int mask) +{ + int err; + aufs_bindex_t bindex, bbot; + const unsigned char isdir = !!S_ISDIR(inode->i_mode), + write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); + struct inode *h_inode; + struct super_block *sb; + struct au_branch *br; + + /* todo: support rcu-walk? */ + if (mask & MAY_NOT_BLOCK) + return -ECHILD; + + sb = inode->i_sb; + si_read_lock(sb, AuLock_FLUSH); + ii_read_lock_child(inode); +#if 0 /* reserved for future use */ + /* + * This test may be rather 'too much' since the test is essentially done + * in the aufs_lookup(). Theoretically it is possible that the inode + * generation doesn't match to the superblock's here. But it isn't a + * big deal I suppose. + */ + err = au_iigen_test(inode, au_sigen(sb)); + if (unlikely(err)) + goto out; +#endif + + if (!isdir + || write_mask + || au_opt_test(au_mntflags(sb), DIRPERM1)) { + err = au_busy_or_stale(); + h_inode = au_h_iptr(inode, au_ibtop(inode)); + if (unlikely(!h_inode + || (h_inode->i_mode & S_IFMT) + != (inode->i_mode & S_IFMT))) + goto out; + + err = 0; + bindex = au_ibtop(inode); + br = au_sbr(sb, bindex); + err = h_permission(h_inode, mask, &br->br_path, br->br_perm); + if (write_mask + && !err + && !special_file(h_inode->i_mode)) { + /* test whether the upper writable branch exists */ + err = -EROFS; + for (; bindex >= 0; bindex--) + if (!au_br_rdonly(au_sbr(sb, bindex))) { + err = 0; + break; + } + } + goto out; + } + + /* non-write to dir */ + err = 0; + bbot = au_ibbot(inode); + for (bindex = au_ibtop(inode); !err && bindex <= bbot; bindex++) { + h_inode = au_h_iptr(inode, bindex); + if (h_inode) { + err = au_busy_or_stale(); + if (unlikely(!S_ISDIR(h_inode->i_mode))) + break; + + br = au_sbr(sb, bindex); + err = h_permission(h_inode, mask, &br->br_path, + br->br_perm); + } + } + +out: + ii_read_unlock(inode); + si_read_unlock(sb); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, + unsigned int flags) +{ + struct dentry *ret, *parent; + struct inode *inode; + struct super_block *sb; + int err, npositive; + + IMustLock(dir); + + /* todo: support rcu-walk? */ + ret = ERR_PTR(-ECHILD); + if (flags & LOOKUP_RCU) + goto out; + + ret = ERR_PTR(-ENAMETOOLONG); + if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) + goto out; + + sb = dir->i_sb; + err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); + ret = ERR_PTR(err); + if (unlikely(err)) + goto out; + + err = au_di_init(dentry); + ret = ERR_PTR(err); + if (unlikely(err)) + goto out_si; + + inode = NULL; + npositive = 0; /* suppress a warning */ + parent = dentry->d_parent; /* dir inode is locked */ + di_read_lock_parent(parent, AuLock_IR); + err = au_alive_dir(parent); + if (!err) + err = au_digen_test(parent, au_sigen(sb)); + if (!err) { + /* regardless LOOKUP_CREATE, always ALLOW_NEG */ + npositive = au_lkup_dentry(dentry, au_dbtop(parent), + AuLkup_ALLOW_NEG); + err = npositive; + } + di_read_unlock(parent, AuLock_IR); + ret = ERR_PTR(err); + if (unlikely(err < 0)) + goto out_unlock; + + if (npositive) { + inode = au_new_inode(dentry, /*must_new*/0); + if (IS_ERR(inode)) { + ret = (void *)inode; + inode = NULL; + goto out_unlock; + } + } + + if (inode) + atomic_inc(&inode->i_count); + ret = d_splice_alias(inode, dentry); +#if 0 /* reserved for future use */ + if (unlikely(d_need_lookup(dentry))) { + spin_lock(&dentry->d_lock); + dentry->d_flags &= ~DCACHE_NEED_LOOKUP; + spin_unlock(&dentry->d_lock); + } else +#endif + if (inode) { + if (!IS_ERR(ret)) { + iput(inode); + if (ret && ret != dentry) + ii_write_unlock(inode); + } else { + ii_write_unlock(inode); + iput(inode); + inode = NULL; + } + } + +out_unlock: + di_write_unlock(dentry); +out_si: + si_read_unlock(sb); +out: + return ret; +} + +/* ---------------------------------------------------------------------- */ + +/* + * very dirty and complicated aufs ->atomic_open(). + * aufs_atomic_open() + * + au_aopen_or_create() + * + add_simple() + * + vfsub_atomic_open() + * + branch fs ->atomic_open() + * may call the actual 'open' for h_file + * + inc br_nfiles only if opened + * + au_aopen_no_open() or au_aopen_do_open() + * + * au_aopen_do_open() + * + finish_open() + * + au_do_aopen() + * + au_do_open() the body of all 'open' + * + au_do_open_nondir() + * set the passed h_file + * + * au_aopen_no_open() + * + finish_no_open() + */ + +struct aopen_node { + struct hlist_bl_node hblist; + struct file *file, *h_file; +}; + +static int au_do_aopen(struct inode *inode, struct file *file) +{ + struct hlist_bl_head *aopen; + struct hlist_bl_node *pos; + struct aopen_node *node; + struct au_do_open_args args = { + .aopen = 1, + .open = au_do_open_nondir + }; + + aopen = &au_sbi(inode->i_sb)->si_aopen; + hlist_bl_lock(aopen); + hlist_bl_for_each_entry(node, pos, aopen, hblist) + if (node->file == file) { + args.h_file = node->h_file; + break; + } + hlist_bl_unlock(aopen); + /* AuDebugOn(!args.h_file); */ + + return au_do_open(file, &args); +} + +static int au_aopen_do_open(struct file *file, struct dentry *dentry, + struct aopen_node *aopen_node) +{ + int err; + struct hlist_bl_head *aopen; + + AuLabel(here); + aopen = &au_sbi(dentry->d_sb)->si_aopen; + au_hbl_add(&aopen_node->hblist, aopen); + err = finish_open(file, dentry, au_do_aopen); + au_hbl_del(&aopen_node->hblist, aopen); + /* AuDbgFile(file); */ + AuDbg("%pd%s%s\n", dentry, + (file->f_mode & FMODE_CREATED) ? " created" : "", + (file->f_mode & FMODE_OPENED) ? " opened" : ""); + + AuTraceErr(err); + return err; +} + +static int au_aopen_no_open(struct file *file, struct dentry *dentry) +{ + int err; + + AuLabel(here); + dget(dentry); + err = finish_no_open(file, dentry); + + AuTraceErr(err); + return err; +} + +static int aufs_atomic_open(struct inode *dir, struct dentry *dentry, + struct file *file, unsigned int open_flag, + umode_t create_mode) +{ + int err, did_open; + unsigned int lkup_flags; + aufs_bindex_t bindex; + struct super_block *sb; + struct dentry *parent, *d; + struct vfsub_aopen_args args = { + .open_flag = open_flag, + .create_mode = create_mode + }; + struct aopen_node aopen_node = { + .file = file + }; + + IMustLock(dir); + AuDbg("open_flag 0%o\n", open_flag); + AuDbgDentry(dentry); + + err = 0; + if (!au_di(dentry)) { + lkup_flags = LOOKUP_OPEN; + if (open_flag & O_CREAT) + lkup_flags |= LOOKUP_CREATE; + d = aufs_lookup(dir, dentry, lkup_flags); + if (IS_ERR(d)) { + err = PTR_ERR(d); + AuTraceErr(err); + goto out; + } else if (d) { + /* + * obsoleted dentry found. + * another error will be returned later. + */ + d_drop(d); + AuDbgDentry(d); + dput(d); + } + AuDbgDentry(dentry); + } + + if (d_is_positive(dentry) + || d_unhashed(dentry) + || d_unlinked(dentry) + || !(open_flag & O_CREAT)) { + err = au_aopen_no_open(file, dentry); + goto out; /* success */ + } + + err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN); + if (unlikely(err)) + goto out; + + sb = dentry->d_sb; + parent = dentry->d_parent; /* dir is locked */ + di_write_lock_parent(parent); + err = au_lkup_dentry(dentry, /*btop*/0, AuLkup_ALLOW_NEG); + if (unlikely(err < 0)) + goto out_parent; + + AuDbgDentry(dentry); + if (d_is_positive(dentry)) { + err = au_aopen_no_open(file, dentry); + goto out_parent; /* success */ + } + + args.file = alloc_empty_file(file->f_flags, current_cred()); + err = PTR_ERR(args.file); + if (IS_ERR(args.file)) + goto out_parent; + + bindex = au_dbtop(dentry); + err = au_aopen_or_create(dir, dentry, &args); + AuTraceErr(err); + AuDbgFile(args.file); + file->f_mode = args.file->f_mode & ~FMODE_OPENED; + did_open = !!(args.file->f_mode & FMODE_OPENED); + if (!did_open) { + fput(args.file); + args.file = NULL; + } + di_write_unlock(parent); + di_write_unlock(dentry); + if (unlikely(err < 0)) { + if (args.file) + fput(args.file); + goto out_sb; + } + + if (!did_open) + err = au_aopen_no_open(file, dentry); + else { + aopen_node.h_file = args.file; + err = au_aopen_do_open(file, dentry, &aopen_node); + } + if (unlikely(err < 0)) { + if (args.file) + fput(args.file); + if (did_open) + au_lcnt_dec(&args.br->br_nfiles); + } + goto out_sb; /* success */ + +out_parent: + di_write_unlock(parent); + di_write_unlock(dentry); +out_sb: + si_read_unlock(sb); +out: + AuTraceErr(err); + AuDbgFile(file); + return err; +} + + +/* ---------------------------------------------------------------------- */ + +static int au_wr_dir_cpup(struct dentry *dentry, struct dentry *parent, + const unsigned char add_entry, aufs_bindex_t bcpup, + aufs_bindex_t btop) +{ + int err; + struct dentry *h_parent; + struct inode *h_dir; + + if (add_entry) + IMustLock(d_inode(parent)); + else + di_write_lock_parent(parent); + + err = 0; + if (!au_h_dptr(parent, bcpup)) { + if (btop > bcpup) + err = au_cpup_dirs(dentry, bcpup); + else if (btop < bcpup) + err = au_cpdown_dirs(dentry, bcpup); + else + BUG(); + } + if (!err && add_entry && !au_ftest_wrdir(add_entry, TMPFILE)) { + h_parent = au_h_dptr(parent, bcpup); + h_dir = d_inode(h_parent); + inode_lock_shared_nested(h_dir, AuLsc_I_PARENT); + err = au_lkup_neg(dentry, bcpup, /*wh*/0); + /* todo: no unlock here */ + inode_unlock_shared(h_dir); + + AuDbg("bcpup %d\n", bcpup); + if (!err) { + if (d_really_is_negative(dentry)) + au_set_h_dptr(dentry, btop, NULL); + au_update_dbrange(dentry, /*do_put_zero*/0); + } + } + + if (!add_entry) + di_write_unlock(parent); + if (!err) + err = bcpup; /* success */ + + AuTraceErr(err); + return err; +} + +/* + * decide the branch and the parent dir where we will create a new entry. + * returns new bindex or an error. + * copyup the parent dir if needed. + */ +int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, + struct au_wr_dir_args *args) +{ + int err; + unsigned int flags; + aufs_bindex_t bcpup, btop, src_btop; + const unsigned char add_entry + = au_ftest_wrdir(args->flags, ADD_ENTRY) + | au_ftest_wrdir(args->flags, TMPFILE); + struct super_block *sb; + struct dentry *parent; + struct au_sbinfo *sbinfo; + + sb = dentry->d_sb; + sbinfo = au_sbi(sb); + parent = dget_parent(dentry); + btop = au_dbtop(dentry); + bcpup = btop; + if (args->force_btgt < 0) { + if (src_dentry) { + src_btop = au_dbtop(src_dentry); + if (src_btop < btop) + bcpup = src_btop; + } else if (add_entry) { + flags = 0; + if (au_ftest_wrdir(args->flags, ISDIR)) + au_fset_wbr(flags, DIR); + err = AuWbrCreate(sbinfo, dentry, flags); + bcpup = err; + } + + if (bcpup < 0 || au_test_ro(sb, bcpup, d_inode(dentry))) { + if (add_entry) + err = AuWbrCopyup(sbinfo, dentry); + else { + if (!IS_ROOT(dentry)) { + di_read_lock_parent(parent, !AuLock_IR); + err = AuWbrCopyup(sbinfo, dentry); + di_read_unlock(parent, !AuLock_IR); + } else + err = AuWbrCopyup(sbinfo, dentry); + } + bcpup = err; + if (unlikely(err < 0)) + goto out; + } + } else { + bcpup = args->force_btgt; + AuDebugOn(au_test_ro(sb, bcpup, d_inode(dentry))); + } + + AuDbg("btop %d, bcpup %d\n", btop, bcpup); + err = bcpup; + if (bcpup == btop) + goto out; /* success */ + + /* copyup the new parent into the branch we process */ + err = au_wr_dir_cpup(dentry, parent, add_entry, bcpup, btop); + if (err >= 0) { + if (d_really_is_negative(dentry)) { + au_set_h_dptr(dentry, btop, NULL); + au_set_dbtop(dentry, bcpup); + au_set_dbbot(dentry, bcpup); + } + AuDebugOn(add_entry + && !au_ftest_wrdir(args->flags, TMPFILE) + && !au_h_dptr(dentry, bcpup)); + } + +out: + dput(parent); + return err; +} + +/* ---------------------------------------------------------------------- */ + +void au_pin_hdir_unlock(struct au_pin *p) +{ + if (p->hdir) + au_hn_inode_unlock(p->hdir); +} + +int au_pin_hdir_lock(struct au_pin *p) +{ + int err; + + err = 0; + if (!p->hdir) + goto out; + + /* even if an error happens later, keep this lock */ + au_hn_inode_lock_nested(p->hdir, p->lsc_hi); + + err = -EBUSY; + if (unlikely(p->hdir->hi_inode != d_inode(p->h_parent))) + goto out; + + err = 0; + if (p->h_dentry) + err = au_h_verify(p->h_dentry, p->udba, p->hdir->hi_inode, + p->h_parent, p->br); + +out: + return err; +} + +int au_pin_hdir_relock(struct au_pin *p) +{ + int err, i; + struct inode *h_i; + struct dentry *h_d[] = { + p->h_dentry, + p->h_parent + }; + + err = au_pin_hdir_lock(p); + if (unlikely(err)) + goto out; + + for (i = 0; !err && i < sizeof(h_d)/sizeof(*h_d); i++) { + if (!h_d[i]) + continue; + if (d_is_positive(h_d[i])) { + h_i = d_inode(h_d[i]); + err = !h_i->i_nlink; + } + } + +out: + return err; +} + +static void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task) +{ + atomic_long_set(&p->hdir->hi_inode->i_rwsem.owner, (long)task); +} + +void au_pin_hdir_acquire_nest(struct au_pin *p) +{ + if (p->hdir) { + rwsem_acquire_nest(&p->hdir->hi_inode->i_rwsem.dep_map, + p->lsc_hi, 0, NULL, _RET_IP_); + au_pin_hdir_set_owner(p, current); + } +} + +void au_pin_hdir_release(struct au_pin *p) +{ + if (p->hdir) { + au_pin_hdir_set_owner(p, p->task); + rwsem_release(&p->hdir->hi_inode->i_rwsem.dep_map, _RET_IP_); + } +} + +struct dentry *au_pinned_h_parent(struct au_pin *pin) +{ + if (pin && pin->parent) + return au_h_dptr(pin->parent, pin->bindex); + return NULL; +} + +void au_unpin(struct au_pin *p) +{ + if (p->hdir) + au_pin_hdir_unlock(p); + if (p->h_mnt && au_ftest_pin(p->flags, MNT_WRITE)) + vfsub_mnt_drop_write(p->h_mnt); + if (!p->hdir) + return; + + if (!au_ftest_pin(p->flags, DI_LOCKED)) + di_read_unlock(p->parent, AuLock_IR); + iput(p->hdir->hi_inode); + dput(p->parent); + p->parent = NULL; + p->hdir = NULL; + p->h_mnt = NULL; + /* do not clear p->task */ +} + +int au_do_pin(struct au_pin *p) +{ + int err; + struct super_block *sb; + struct inode *h_dir; + + err = 0; + sb = p->dentry->d_sb; + p->br = au_sbr(sb, p->bindex); + if (IS_ROOT(p->dentry)) { + if (au_ftest_pin(p->flags, MNT_WRITE)) { + p->h_mnt = au_br_mnt(p->br); + err = vfsub_mnt_want_write(p->h_mnt); + if (unlikely(err)) { + au_fclr_pin(p->flags, MNT_WRITE); + goto out_err; + } + } + goto out; + } + + p->h_dentry = NULL; + if (p->bindex <= au_dbbot(p->dentry)) + p->h_dentry = au_h_dptr(p->dentry, p->bindex); + + p->parent = dget_parent(p->dentry); + if (!au_ftest_pin(p->flags, DI_LOCKED)) + di_read_lock(p->parent, AuLock_IR, p->lsc_di); + + h_dir = NULL; + p->h_parent = au_h_dptr(p->parent, p->bindex); + p->hdir = au_hi(d_inode(p->parent), p->bindex); + if (p->hdir) + h_dir = p->hdir->hi_inode; + + /* + * udba case, or + * if DI_LOCKED is not set, then p->parent may be different + * and h_parent can be NULL. + */ + if (unlikely(!p->hdir || !h_dir || !p->h_parent)) { + err = -EBUSY; + if (!au_ftest_pin(p->flags, DI_LOCKED)) + di_read_unlock(p->parent, AuLock_IR); + dput(p->parent); + p->parent = NULL; + goto out_err; + } + + if (au_ftest_pin(p->flags, MNT_WRITE)) { + p->h_mnt = au_br_mnt(p->br); + err = vfsub_mnt_want_write(p->h_mnt); + if (unlikely(err)) { + au_fclr_pin(p->flags, MNT_WRITE); + if (!au_ftest_pin(p->flags, DI_LOCKED)) + di_read_unlock(p->parent, AuLock_IR); + dput(p->parent); + p->parent = NULL; + goto out_err; + } + } + + au_igrab(h_dir); + err = au_pin_hdir_lock(p); + if (!err) + goto out; /* success */ + + au_unpin(p); + +out_err: + pr_err("err %d\n", err); + err = au_busy_or_stale(); +out: + return err; +} + +void au_pin_init(struct au_pin *p, struct dentry *dentry, + aufs_bindex_t bindex, int lsc_di, int lsc_hi, + unsigned int udba, unsigned char flags) +{ + p->dentry = dentry; + p->udba = udba; + p->lsc_di = lsc_di; + p->lsc_hi = lsc_hi; + p->flags = flags; + p->bindex = bindex; + + p->parent = NULL; + p->hdir = NULL; + p->h_mnt = NULL; + + p->h_dentry = NULL; + p->h_parent = NULL; + p->br = NULL; + p->task = current; +} + +int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex, + unsigned int udba, unsigned char flags) +{ + au_pin_init(pin, dentry, bindex, AuLsc_DI_PARENT, AuLsc_I_PARENT2, + udba, flags); + return au_do_pin(pin); +} + +/* ---------------------------------------------------------------------- */ + +/* + * ->setattr() and ->getattr() are called in various cases. + * chmod, stat: dentry is revalidated. + * fchmod, fstat: file and dentry are not revalidated, additionally they may be + * unhashed. + * for ->setattr(), ia->ia_file is passed from ftruncate only. + */ +/* todo: consolidate with do_refresh() and simple_reval_dpath() */ +int au_reval_for_attr(struct dentry *dentry, unsigned int sigen) +{ + int err; + struct dentry *parent; + + err = 0; + if (au_digen_test(dentry, sigen)) { + parent = dget_parent(dentry); + di_read_lock_parent(parent, AuLock_IR); + err = au_refresh_dentry(dentry, parent); + di_read_unlock(parent, AuLock_IR); + dput(parent); + } + + AuTraceErr(err); + return err; +} + +int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia, + struct au_icpup_args *a) +{ + int err; + loff_t sz; + aufs_bindex_t btop, ibtop; + struct dentry *hi_wh, *parent; + struct inode *inode; + struct au_wr_dir_args wr_dir_args = { + .force_btgt = -1, + .flags = 0 + }; + + if (d_is_dir(dentry)) + au_fset_wrdir(wr_dir_args.flags, ISDIR); + /* plink or hi_wh() case */ + btop = au_dbtop(dentry); + inode = d_inode(dentry); + ibtop = au_ibtop(inode); + if (btop != ibtop && !au_test_ro(inode->i_sb, ibtop, inode)) + wr_dir_args.force_btgt = ibtop; + err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args); + if (unlikely(err < 0)) + goto out; + a->btgt = err; + if (err != btop) + au_fset_icpup(a->flags, DID_CPUP); + + err = 0; + a->pin_flags = AuPin_MNT_WRITE; + parent = NULL; + if (!IS_ROOT(dentry)) { + au_fset_pin(a->pin_flags, DI_LOCKED); + parent = dget_parent(dentry); + di_write_lock_parent(parent); + } + + err = au_pin(&a->pin, dentry, a->btgt, a->udba, a->pin_flags); + if (unlikely(err)) + goto out_parent; + + sz = -1; + a->h_path.dentry = au_h_dptr(dentry, btop); + a->h_inode = d_inode(a->h_path.dentry); + if (ia && (ia->ia_valid & ATTR_SIZE)) { + inode_lock_shared_nested(a->h_inode, AuLsc_I_CHILD); + if (ia->ia_size < i_size_read(a->h_inode)) + sz = ia->ia_size; + inode_unlock_shared(a->h_inode); + } + + hi_wh = NULL; + if (au_ftest_icpup(a->flags, DID_CPUP) && d_unlinked(dentry)) { + hi_wh = au_hi_wh(inode, a->btgt); + if (!hi_wh) { + struct au_cp_generic cpg = { + .dentry = dentry, + .bdst = a->btgt, + .bsrc = -1, + .len = sz, + .pin = &a->pin + }; + err = au_sio_cpup_wh(&cpg, /*file*/NULL); + if (unlikely(err)) + goto out_unlock; + hi_wh = au_hi_wh(inode, a->btgt); + /* todo: revalidate hi_wh? */ + } + } + + if (parent) { + au_pin_set_parent_lflag(&a->pin, /*lflag*/0); + di_downgrade_lock(parent, AuLock_IR); + dput(parent); + parent = NULL; + } + if (!au_ftest_icpup(a->flags, DID_CPUP)) + goto out; /* success */ + + if (!d_unhashed(dentry)) { + struct au_cp_generic cpg = { + .dentry = dentry, + .bdst = a->btgt, + .bsrc = btop, + .len = sz, + .pin = &a->pin, + .flags = AuCpup_DTIME | AuCpup_HOPEN + }; + err = au_sio_cpup_simple(&cpg); + if (!err) + a->h_path.dentry = au_h_dptr(dentry, a->btgt); + } else if (!hi_wh) + a->h_path.dentry = au_h_dptr(dentry, a->btgt); + else + a->h_path.dentry = hi_wh; /* do not dget here */ + +out_unlock: + a->h_inode = d_inode(a->h_path.dentry); + if (!err) + goto out; /* success */ + au_unpin(&a->pin); +out_parent: + if (parent) { + di_write_unlock(parent); + dput(parent); + } +out: + if (!err) + inode_lock_nested(a->h_inode, AuLsc_I_CHILD); + return err; +} + +static int aufs_setattr(struct dentry *dentry, struct iattr *ia) +{ + int err; + struct inode *inode, *delegated; + struct super_block *sb; + struct file *file; + struct au_icpup_args *a; + + inode = d_inode(dentry); + IMustLock(inode); + + err = setattr_prepare(dentry, ia); + if (unlikely(err)) + goto out; + + err = -ENOMEM; + a = kzalloc(sizeof(*a), GFP_NOFS); + if (unlikely(!a)) + goto out; + + if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) + ia->ia_valid &= ~ATTR_MODE; + + file = NULL; + sb = dentry->d_sb; + err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); + if (unlikely(err)) + goto out_kfree; + + if (ia->ia_valid & ATTR_FILE) { + /* currently ftruncate(2) only */ + AuDebugOn(!d_is_reg(dentry)); + file = ia->ia_file; + err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1, + /*fi_lsc*/0); + if (unlikely(err)) + goto out_si; + ia->ia_file = au_hf_top(file); + a->udba = AuOpt_UDBA_NONE; + } else { + /* fchmod() doesn't pass ia_file */ + a->udba = au_opt_udba(sb); + di_write_lock_child(dentry); + /* no d_unlinked(), to set UDBA_NONE for root */ + if (d_unhashed(dentry)) + a->udba = AuOpt_UDBA_NONE; + if (a->udba != AuOpt_UDBA_NONE) { + AuDebugOn(IS_ROOT(dentry)); + err = au_reval_for_attr(dentry, au_sigen(sb)); + if (unlikely(err)) + goto out_dentry; + } + } + + err = au_pin_and_icpup(dentry, ia, a); + if (unlikely(err < 0)) + goto out_dentry; + if (au_ftest_icpup(a->flags, DID_CPUP)) { + ia->ia_file = NULL; + ia->ia_valid &= ~ATTR_FILE; + } + + a->h_path.mnt = au_sbr_mnt(sb, a->btgt); + if ((ia->ia_valid & (ATTR_MODE | ATTR_CTIME)) + == (ATTR_MODE | ATTR_CTIME)) { + err = security_path_chmod(&a->h_path, ia->ia_mode); + if (unlikely(err)) + goto out_unlock; + } else if ((ia->ia_valid & (ATTR_UID | ATTR_GID)) + && (ia->ia_valid & ATTR_CTIME)) { + err = security_path_chown(&a->h_path, ia->ia_uid, ia->ia_gid); + if (unlikely(err)) + goto out_unlock; + } + + if (ia->ia_valid & ATTR_SIZE) { + struct file *f; + + if (ia->ia_size < i_size_read(inode)) + /* unmap only */ + truncate_setsize(inode, ia->ia_size); + + f = NULL; + if (ia->ia_valid & ATTR_FILE) + f = ia->ia_file; + inode_unlock(a->h_inode); + err = vfsub_trunc(&a->h_path, ia->ia_size, ia->ia_valid, f); + inode_lock_nested(a->h_inode, AuLsc_I_CHILD); + } else { + delegated = NULL; + while (1) { + err = vfsub_notify_change(&a->h_path, ia, &delegated); + if (delegated) { + err = break_deleg_wait(&delegated); + if (!err) + continue; + } + break; + } + } + /* + * regardless aufs 'acl' option setting. + * why don't all acl-aware fs call this func from their ->setattr()? + */ + if (!err && (ia->ia_valid & ATTR_MODE)) + err = vfsub_acl_chmod(a->h_inode, ia->ia_mode); + if (!err) + au_cpup_attr_changeable(inode); + +out_unlock: + inode_unlock(a->h_inode); + au_unpin(&a->pin); + if (unlikely(err)) + au_update_dbtop(dentry); +out_dentry: + di_write_unlock(dentry); + if (file) { + fi_write_unlock(file); + ia->ia_file = file; + ia->ia_valid |= ATTR_FILE; + } +out_si: + si_read_unlock(sb); +out_kfree: + au_kfree_rcu(a); +out: + AuTraceErr(err); + return err; +} + +#if IS_ENABLED(CONFIG_AUFS_XATTR) || IS_ENABLED(CONFIG_FS_POSIX_ACL) +static int au_h_path_to_set_attr(struct dentry *dentry, + struct au_icpup_args *a, struct path *h_path) +{ + int err; + struct super_block *sb; + + sb = dentry->d_sb; + a->udba = au_opt_udba(sb); + /* no d_unlinked(), to set UDBA_NONE for root */ + if (d_unhashed(dentry)) + a->udba = AuOpt_UDBA_NONE; + if (a->udba != AuOpt_UDBA_NONE) { + AuDebugOn(IS_ROOT(dentry)); + err = au_reval_for_attr(dentry, au_sigen(sb)); + if (unlikely(err)) + goto out; + } + err = au_pin_and_icpup(dentry, /*ia*/NULL, a); + if (unlikely(err < 0)) + goto out; + + h_path->dentry = a->h_path.dentry; + h_path->mnt = au_sbr_mnt(sb, a->btgt); + +out: + return err; +} + +ssize_t au_sxattr(struct dentry *dentry, struct inode *inode, + struct au_sxattr *arg) +{ + int err; + struct path h_path; + struct super_block *sb; + struct au_icpup_args *a; + struct inode *h_inode; + + IMustLock(inode); + + err = -ENOMEM; + a = kzalloc(sizeof(*a), GFP_NOFS); + if (unlikely(!a)) + goto out; + + sb = dentry->d_sb; + err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); + if (unlikely(err)) + goto out_kfree; + + h_path.dentry = NULL; /* silence gcc */ + di_write_lock_child(dentry); + err = au_h_path_to_set_attr(dentry, a, &h_path); + if (unlikely(err)) + goto out_di; + + inode_unlock(a->h_inode); + switch (arg->type) { + case AU_XATTR_SET: + AuDebugOn(d_is_negative(h_path.dentry)); + err = vfsub_setxattr(h_path.dentry, + arg->u.set.name, arg->u.set.value, + arg->u.set.size, arg->u.set.flags); + break; + case AU_ACL_SET: + err = -EOPNOTSUPP; + h_inode = d_inode(h_path.dentry); + if (h_inode->i_op->set_acl) + /* this will call posix_acl_update_mode */ + err = h_inode->i_op->set_acl(h_inode, + arg->u.acl_set.acl, + arg->u.acl_set.type); + break; + } + if (!err) + au_cpup_attr_timesizes(inode); + + au_unpin(&a->pin); + if (unlikely(err)) + au_update_dbtop(dentry); + +out_di: + di_write_unlock(dentry); + si_read_unlock(sb); +out_kfree: + au_kfree_rcu(a); +out: + AuTraceErr(err); + return err; +} +#endif + +static void au_refresh_iattr(struct inode *inode, struct kstat *st, + unsigned int nlink) +{ + unsigned int n; + + inode->i_mode = st->mode; + /* don't i_[ug]id_write() here */ + inode->i_uid = st->uid; + inode->i_gid = st->gid; + inode->i_atime = st->atime; + inode->i_mtime = st->mtime; + inode->i_ctime = st->ctime; + + au_cpup_attr_nlink(inode, /*force*/0); + if (S_ISDIR(inode->i_mode)) { + n = inode->i_nlink; + n -= nlink; + n += st->nlink; + smp_mb(); /* for i_nlink */ + /* 0 can happen */ + set_nlink(inode, n); + } + + spin_lock(&inode->i_lock); + inode->i_blocks = st->blocks; + i_size_write(inode, st->size); + spin_unlock(&inode->i_lock); +} + +/* + * common routine for aufs_getattr() and au_getxattr(). + * returns zero or negative (an error). + * @dentry will be read-locked in success. + */ +int au_h_path_getattr(struct dentry *dentry, struct inode *inode, int force, + struct path *h_path, int locked) +{ + int err; + unsigned int mnt_flags, sigen; + unsigned char udba_none; + aufs_bindex_t bindex; + struct super_block *sb, *h_sb; + + h_path->mnt = NULL; + h_path->dentry = NULL; + + err = 0; + sb = dentry->d_sb; + mnt_flags = au_mntflags(sb); + udba_none = !!au_opt_test(mnt_flags, UDBA_NONE); + + if (unlikely(locked)) + goto body; /* skip locking dinfo */ + + /* support fstat(2) */ + if (!d_unlinked(dentry) && !udba_none) { + sigen = au_sigen(sb); + err = au_digen_test(dentry, sigen); + if (!err) { + di_read_lock_child(dentry, AuLock_IR); + err = au_dbrange_test(dentry); + if (unlikely(err)) { + di_read_unlock(dentry, AuLock_IR); + goto out; + } + } else { + AuDebugOn(IS_ROOT(dentry)); + di_write_lock_child(dentry); + err = au_dbrange_test(dentry); + if (!err) + err = au_reval_for_attr(dentry, sigen); + if (!err) + di_downgrade_lock(dentry, AuLock_IR); + else { + di_write_unlock(dentry); + goto out; + } + } + } else + di_read_lock_child(dentry, AuLock_IR); + +body: + if (!inode) { + inode = d_inode(dentry); + if (unlikely(!inode)) + goto out; + } + bindex = au_ibtop(inode); + h_path->mnt = au_sbr_mnt(sb, bindex); + h_sb = h_path->mnt->mnt_sb; + if (!force + && !au_test_fs_bad_iattr(h_sb) + && udba_none) + goto out; /* success */ + + if (au_dbtop(dentry) == bindex) + h_path->dentry = au_h_dptr(dentry, bindex); + else if (au_opt_test(mnt_flags, PLINK) && au_plink_test(inode)) { + h_path->dentry = au_plink_lkup(inode, bindex); + if (IS_ERR(h_path->dentry)) + /* pretending success */ + h_path->dentry = NULL; + else + dput(h_path->dentry); + } + +out: + return err; +} + +static int aufs_getattr(const struct path *path, struct kstat *st, + u32 request, unsigned int query) +{ + int err; + unsigned char positive; + struct path h_path; + struct dentry *dentry; + struct inode *inode; + struct super_block *sb; + + dentry = path->dentry; + inode = d_inode(dentry); + sb = dentry->d_sb; + err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); + if (unlikely(err)) + goto out; + err = au_h_path_getattr(dentry, /*inode*/NULL, /*force*/0, &h_path, + /*locked*/0); + if (unlikely(err)) + goto out_si; + if (unlikely(!h_path.dentry)) + /* illegally overlapped or something */ + goto out_fill; /* pretending success */ + + positive = d_is_positive(h_path.dentry); + if (positive) + /* no vfsub version */ + err = vfs_getattr(&h_path, st, request, query); + if (!err) { + if (positive) + au_refresh_iattr(inode, st, + d_inode(h_path.dentry)->i_nlink); + goto out_fill; /* success */ + } + AuTraceErr(err); + goto out_di; + +out_fill: + generic_fillattr(inode, st); +out_di: + di_read_unlock(dentry, AuLock_IR); +out_si: + si_read_unlock(sb); +out: + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static const char *aufs_get_link(struct dentry *dentry, struct inode *inode, + struct delayed_call *done) +{ + const char *ret; + struct dentry *h_dentry; + struct inode *h_inode; + int err; + aufs_bindex_t bindex; + + ret = NULL; /* suppress a warning */ + err = -ECHILD; + if (!dentry) + goto out; + + err = aufs_read_lock(dentry, AuLock_IR | AuLock_GEN); + if (unlikely(err)) + goto out; + + err = au_d_hashed_positive(dentry); + if (unlikely(err)) + goto out_unlock; + + err = -EINVAL; + inode = d_inode(dentry); + bindex = au_ibtop(inode); + h_inode = au_h_iptr(inode, bindex); + if (unlikely(!h_inode->i_op->get_link)) + goto out_unlock; + + err = -EBUSY; + h_dentry = NULL; + if (au_dbtop(dentry) <= bindex) { + h_dentry = au_h_dptr(dentry, bindex); + if (h_dentry) + dget(h_dentry); + } + if (!h_dentry) { + h_dentry = d_find_any_alias(h_inode); + if (IS_ERR(h_dentry)) { + err = PTR_ERR(h_dentry); + goto out_unlock; + } + } + if (unlikely(!h_dentry)) + goto out_unlock; + + err = 0; + AuDbg("%ps\n", h_inode->i_op->get_link); + AuDbgDentry(h_dentry); + ret = vfs_get_link(h_dentry, done); + dput(h_dentry); + if (IS_ERR(ret)) + err = PTR_ERR(ret); + +out_unlock: + aufs_read_unlock(dentry, AuLock_IR); +out: + if (unlikely(err)) + ret = ERR_PTR(err); + AuTraceErrPtr(ret); + return ret; +} + +/* ---------------------------------------------------------------------- */ + +static int au_is_special(struct inode *inode) +{ + return (inode->i_mode & (S_IFBLK | S_IFCHR | S_IFIFO | S_IFSOCK)); +} + +static int aufs_update_time(struct inode *inode, struct timespec64 *ts, + int flags) +{ + int err; + aufs_bindex_t bindex; + struct super_block *sb; + struct inode *h_inode; + struct vfsmount *h_mnt; + + sb = inode->i_sb; + WARN_ONCE((flags & S_ATIME) && !IS_NOATIME(inode), + "unexpected s_flags 0x%lx", sb->s_flags); + + /* mmap_sem might be acquired already, cf. aufs_mmap() */ + lockdep_off(); + si_read_lock(sb, AuLock_FLUSH); + ii_write_lock_child(inode); + + err = 0; + bindex = au_ibtop(inode); + h_inode = au_h_iptr(inode, bindex); + if (!au_test_ro(sb, bindex, inode)) { + h_mnt = au_sbr_mnt(sb, bindex); + err = vfsub_mnt_want_write(h_mnt); + if (!err) { + err = vfsub_update_time(h_inode, ts, flags); + vfsub_mnt_drop_write(h_mnt); + } + } else if (au_is_special(h_inode)) { + /* + * Never copy-up here. + * These special files may already be opened and used for + * communicating. If we copied it up, then the communication + * would be corrupted. + */ + AuWarn1("timestamps for i%lu are ignored " + "since it is on readonly branch (hi%lu).\n", + inode->i_ino, h_inode->i_ino); + } else if (flags & ~S_ATIME) { + err = -EIO; + AuIOErr1("unexpected flags 0x%x\n", flags); + AuDebugOn(1); + } + + if (!err) + au_cpup_attr_timesizes(inode); + ii_write_unlock(inode); + si_read_unlock(sb); + lockdep_on(); + + if (!err && (flags & S_VERSION)) + inode_inc_iversion(inode); + + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* no getattr version will be set by module.c:aufs_init() */ +struct inode_operations aufs_iop_nogetattr[AuIop_Last], + aufs_iop[] = { + [AuIop_SYMLINK] = { + .permission = aufs_permission, +#ifdef CONFIG_FS_POSIX_ACL + .get_acl = aufs_get_acl, + .set_acl = aufs_set_acl, /* unsupport for symlink? */ +#endif + + .setattr = aufs_setattr, + .getattr = aufs_getattr, + +#ifdef CONFIG_AUFS_XATTR + .listxattr = aufs_listxattr, +#endif + + .get_link = aufs_get_link, + + /* .update_time = aufs_update_time */ + }, + [AuIop_DIR] = { + .create = aufs_create, + .lookup = aufs_lookup, + .link = aufs_link, + .unlink = aufs_unlink, + .symlink = aufs_symlink, + .mkdir = aufs_mkdir, + .rmdir = aufs_rmdir, + .mknod = aufs_mknod, + .rename = aufs_rename, + + .permission = aufs_permission, +#ifdef CONFIG_FS_POSIX_ACL + .get_acl = aufs_get_acl, + .set_acl = aufs_set_acl, +#endif + + .setattr = aufs_setattr, + .getattr = aufs_getattr, + +#ifdef CONFIG_AUFS_XATTR + .listxattr = aufs_listxattr, +#endif + + .update_time = aufs_update_time, + .atomic_open = aufs_atomic_open, + .tmpfile = aufs_tmpfile + }, + [AuIop_OTHER] = { + .permission = aufs_permission, +#ifdef CONFIG_FS_POSIX_ACL + .get_acl = aufs_get_acl, + .set_acl = aufs_set_acl, +#endif + + .setattr = aufs_setattr, + .getattr = aufs_getattr, + +#ifdef CONFIG_AUFS_XATTR + .listxattr = aufs_listxattr, +#endif + + .update_time = aufs_update_time + } +}; diff --git a/fs/aufs/i_op_add.c b/fs/aufs/i_op_add.c new file mode 100644 index 0000000000000..0f738e83386db --- /dev/null +++ b/fs/aufs/i_op_add.c @@ -0,0 +1,936 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * inode operations (add entry) + */ + +#include +#include "aufs.h" + +/* + * final procedure of adding a new entry, except link(2). + * remove whiteout, instantiate, copyup the parent dir's times and size + * and update version. + * if it failed, re-create the removed whiteout. + */ +static int epilog(struct inode *dir, aufs_bindex_t bindex, + struct dentry *wh_dentry, struct dentry *dentry) +{ + int err, rerr; + aufs_bindex_t bwh; + struct path h_path; + struct super_block *sb; + struct inode *inode, *h_dir; + struct dentry *wh; + + bwh = -1; + sb = dir->i_sb; + if (wh_dentry) { + h_dir = d_inode(wh_dentry->d_parent); /* dir inode is locked */ + IMustLock(h_dir); + AuDebugOn(au_h_iptr(dir, bindex) != h_dir); + bwh = au_dbwh(dentry); + h_path.dentry = wh_dentry; + h_path.mnt = au_sbr_mnt(sb, bindex); + err = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, + dentry); + if (unlikely(err)) + goto out; + } + + inode = au_new_inode(dentry, /*must_new*/1); + if (!IS_ERR(inode)) { + d_instantiate(dentry, inode); + dir = d_inode(dentry->d_parent); /* dir inode is locked */ + IMustLock(dir); + au_dir_ts(dir, bindex); + inode_inc_iversion(dir); + au_fhsm_wrote(sb, bindex, /*force*/0); + return 0; /* success */ + } + + err = PTR_ERR(inode); + if (!wh_dentry) + goto out; + + /* revert */ + /* dir inode is locked */ + wh = au_wh_create(dentry, bwh, wh_dentry->d_parent); + rerr = PTR_ERR(wh); + if (IS_ERR(wh)) { + AuIOErr("%pd reverting whiteout failed(%d, %d)\n", + dentry, err, rerr); + err = -EIO; + } else + dput(wh); + +out: + return err; +} + +static int au_d_may_add(struct dentry *dentry) +{ + int err; + + err = 0; + if (unlikely(d_unhashed(dentry))) + err = -ENOENT; + if (unlikely(d_really_is_positive(dentry))) + err = -EEXIST; + return err; +} + +/* + * simple tests for the adding inode operations. + * following the checks in vfs, plus the parent-child relationship. + */ +int au_may_add(struct dentry *dentry, aufs_bindex_t bindex, + struct dentry *h_parent, int isdir) +{ + int err; + umode_t h_mode; + struct dentry *h_dentry; + struct inode *h_inode; + + err = -ENAMETOOLONG; + if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) + goto out; + + h_dentry = au_h_dptr(dentry, bindex); + if (d_really_is_negative(dentry)) { + err = -EEXIST; + if (unlikely(d_is_positive(h_dentry))) + goto out; + } else { + /* rename(2) case */ + err = -EIO; + if (unlikely(d_is_negative(h_dentry))) + goto out; + h_inode = d_inode(h_dentry); + if (unlikely(!h_inode->i_nlink)) + goto out; + + h_mode = h_inode->i_mode; + if (!isdir) { + err = -EISDIR; + if (unlikely(S_ISDIR(h_mode))) + goto out; + } else if (unlikely(!S_ISDIR(h_mode))) { + err = -ENOTDIR; + goto out; + } + } + + err = 0; + /* expected parent dir is locked */ + if (unlikely(h_parent != h_dentry->d_parent)) + err = -EIO; + +out: + AuTraceErr(err); + return err; +} + +/* + * initial procedure of adding a new entry. + * prepare writable branch and the parent dir, lock it, + * and lookup whiteout for the new entry. + */ +static struct dentry* +lock_hdir_lkup_wh(struct dentry *dentry, struct au_dtime *dt, + struct dentry *src_dentry, struct au_pin *pin, + struct au_wr_dir_args *wr_dir_args) +{ + struct dentry *wh_dentry, *h_parent; + struct super_block *sb; + struct au_branch *br; + int err; + unsigned int udba; + aufs_bindex_t bcpup; + + AuDbg("%pd\n", dentry); + + err = au_wr_dir(dentry, src_dentry, wr_dir_args); + bcpup = err; + wh_dentry = ERR_PTR(err); + if (unlikely(err < 0)) + goto out; + + sb = dentry->d_sb; + udba = au_opt_udba(sb); + err = au_pin(pin, dentry, bcpup, udba, + AuPin_DI_LOCKED | AuPin_MNT_WRITE); + wh_dentry = ERR_PTR(err); + if (unlikely(err)) + goto out; + + h_parent = au_pinned_h_parent(pin); + if (udba != AuOpt_UDBA_NONE + && au_dbtop(dentry) == bcpup) + err = au_may_add(dentry, bcpup, h_parent, + au_ftest_wrdir(wr_dir_args->flags, ISDIR)); + else if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) + err = -ENAMETOOLONG; + wh_dentry = ERR_PTR(err); + if (unlikely(err)) + goto out_unpin; + + br = au_sbr(sb, bcpup); + if (dt) { + struct path tmp = { + .dentry = h_parent, + .mnt = au_br_mnt(br) + }; + au_dtime_store(dt, au_pinned_parent(pin), &tmp); + } + + wh_dentry = NULL; + if (bcpup != au_dbwh(dentry)) + goto out; /* success */ + + /* + * ENAMETOOLONG here means that if we allowed create such name, then it + * would not be able to removed in the future. So we don't allow such + * name here and we don't handle ENAMETOOLONG differently here. + */ + wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, br); + +out_unpin: + if (IS_ERR(wh_dentry)) + au_unpin(pin); +out: + return wh_dentry; +} + +/* ---------------------------------------------------------------------- */ + +enum { Mknod, Symlink, Creat }; +struct simple_arg { + int type; + union { + struct { + umode_t mode; + bool want_excl; + bool try_aopen; + struct vfsub_aopen_args *aopen; + } c; + struct { + const char *symname; + } s; + struct { + umode_t mode; + dev_t dev; + } m; + } u; +}; + +static int add_simple(struct inode *dir, struct dentry *dentry, + struct simple_arg *arg) +{ + int err, rerr; + aufs_bindex_t btop; + unsigned char created; + const unsigned char try_aopen + = (arg->type == Creat && arg->u.c.try_aopen); + struct vfsub_aopen_args *aopen = arg->u.c.aopen; + struct dentry *wh_dentry, *parent; + struct inode *h_dir; + struct super_block *sb; + struct au_branch *br; + /* to reduce stack size */ + struct { + struct au_dtime dt; + struct au_pin pin; + struct path h_path; + struct au_wr_dir_args wr_dir_args; + } *a; + + AuDbg("%pd\n", dentry); + IMustLock(dir); + + err = -ENOMEM; + a = kmalloc(sizeof(*a), GFP_NOFS); + if (unlikely(!a)) + goto out; + a->wr_dir_args.force_btgt = -1; + a->wr_dir_args.flags = AuWrDir_ADD_ENTRY; + + parent = dentry->d_parent; /* dir inode is locked */ + if (!try_aopen) { + err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); + if (unlikely(err)) + goto out_free; + } + err = au_d_may_add(dentry); + if (unlikely(err)) + goto out_unlock; + if (!try_aopen) + di_write_lock_parent(parent); + wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL, + &a->pin, &a->wr_dir_args); + err = PTR_ERR(wh_dentry); + if (IS_ERR(wh_dentry)) + goto out_parent; + + btop = au_dbtop(dentry); + sb = dentry->d_sb; + br = au_sbr(sb, btop); + a->h_path.dentry = au_h_dptr(dentry, btop); + a->h_path.mnt = au_br_mnt(br); + h_dir = au_pinned_h_dir(&a->pin); + switch (arg->type) { + case Creat: + if (!try_aopen || !h_dir->i_op->atomic_open) { + err = vfsub_create(h_dir, &a->h_path, arg->u.c.mode, + arg->u.c.want_excl); + created = !err; + if (!err && try_aopen) + aopen->file->f_mode |= FMODE_CREATED; + } else { + aopen->br = br; + err = vfsub_atomic_open(h_dir, a->h_path.dentry, aopen); + AuDbg("err %d\n", err); + AuDbgFile(aopen->file); + created = err >= 0 + && !!(aopen->file->f_mode & FMODE_CREATED); + } + break; + case Symlink: + err = vfsub_symlink(h_dir, &a->h_path, arg->u.s.symname); + created = !err; + break; + case Mknod: + err = vfsub_mknod(h_dir, &a->h_path, arg->u.m.mode, + arg->u.m.dev); + created = !err; + break; + default: + BUG(); + } + if (unlikely(err < 0)) + goto out_unpin; + + err = epilog(dir, btop, wh_dentry, dentry); + if (!err) + goto out_unpin; /* success */ + + /* revert */ + if (created /* && d_is_positive(a->h_path.dentry) */) { + /* no delegation since it is just created */ + rerr = vfsub_unlink(h_dir, &a->h_path, /*delegated*/NULL, + /*force*/0); + if (rerr) { + AuIOErr("%pd revert failure(%d, %d)\n", + dentry, err, rerr); + err = -EIO; + } + au_dtime_revert(&a->dt); + } + if (try_aopen && h_dir->i_op->atomic_open + && (aopen->file->f_mode & FMODE_OPENED)) + /* aopen->file is still opened */ + au_lcnt_dec(&aopen->br->br_nfiles); + +out_unpin: + au_unpin(&a->pin); + dput(wh_dentry); +out_parent: + if (!try_aopen) + di_write_unlock(parent); +out_unlock: + if (unlikely(err)) { + au_update_dbtop(dentry); + d_drop(dentry); + } + if (!try_aopen) + aufs_read_unlock(dentry, AuLock_DW); +out_free: + au_kfree_rcu(a); +out: + return err; +} + +int aufs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, + dev_t dev) +{ + struct simple_arg arg = { + .type = Mknod, + .u.m = { + .mode = mode, + .dev = dev + } + }; + return add_simple(dir, dentry, &arg); +} + +int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) +{ + struct simple_arg arg = { + .type = Symlink, + .u.s.symname = symname + }; + return add_simple(dir, dentry, &arg); +} + +int aufs_create(struct inode *dir, struct dentry *dentry, umode_t mode, + bool want_excl) +{ + struct simple_arg arg = { + .type = Creat, + .u.c = { + .mode = mode, + .want_excl = want_excl + } + }; + return add_simple(dir, dentry, &arg); +} + +int au_aopen_or_create(struct inode *dir, struct dentry *dentry, + struct vfsub_aopen_args *aopen_args) +{ + struct simple_arg arg = { + .type = Creat, + .u.c = { + .mode = aopen_args->create_mode, + .want_excl = aopen_args->open_flag & O_EXCL, + .try_aopen = true, + .aopen = aopen_args + } + }; + return add_simple(dir, dentry, &arg); +} + +int aufs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + int err; + aufs_bindex_t bindex; + struct super_block *sb; + struct dentry *parent, *h_parent, *h_dentry; + struct inode *h_dir, *inode; + struct vfsmount *h_mnt; + struct au_wr_dir_args wr_dir_args = { + .force_btgt = -1, + .flags = AuWrDir_TMPFILE + }; + + /* copy-up may happen */ + inode_lock(dir); + + sb = dir->i_sb; + err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); + if (unlikely(err)) + goto out; + + err = au_di_init(dentry); + if (unlikely(err)) + goto out_si; + + err = -EBUSY; + parent = d_find_any_alias(dir); + AuDebugOn(!parent); + di_write_lock_parent(parent); + if (unlikely(d_inode(parent) != dir)) + goto out_parent; + + err = au_digen_test(parent, au_sigen(sb)); + if (unlikely(err)) + goto out_parent; + + bindex = au_dbtop(parent); + au_set_dbtop(dentry, bindex); + au_set_dbbot(dentry, bindex); + err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args); + bindex = err; + if (unlikely(err < 0)) + goto out_parent; + + err = -EOPNOTSUPP; + h_dir = au_h_iptr(dir, bindex); + if (unlikely(!h_dir->i_op->tmpfile)) + goto out_parent; + + h_mnt = au_sbr_mnt(sb, bindex); + err = vfsub_mnt_want_write(h_mnt); + if (unlikely(err)) + goto out_parent; + + h_parent = au_h_dptr(parent, bindex); + h_dentry = vfs_tmpfile(h_parent, mode, /*open_flag*/0); + if (IS_ERR(h_dentry)) { + err = PTR_ERR(h_dentry); + goto out_mnt; + } + + au_set_dbtop(dentry, bindex); + au_set_dbbot(dentry, bindex); + au_set_h_dptr(dentry, bindex, dget(h_dentry)); + inode = au_new_inode(dentry, /*must_new*/1); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + au_set_h_dptr(dentry, bindex, NULL); + au_set_dbtop(dentry, -1); + au_set_dbbot(dentry, -1); + } else { + if (!inode->i_nlink) + set_nlink(inode, 1); + d_tmpfile(dentry, inode); + au_di(dentry)->di_tmpfile = 1; + + /* update without i_mutex */ + if (au_ibtop(dir) == au_dbtop(dentry)) + au_cpup_attr_timesizes(dir); + } + dput(h_dentry); + +out_mnt: + vfsub_mnt_drop_write(h_mnt); +out_parent: + di_write_unlock(parent); + dput(parent); + di_write_unlock(dentry); + if (unlikely(err)) { + au_di_fin(dentry); + dentry->d_fsdata = NULL; + } +out_si: + si_read_unlock(sb); +out: + inode_unlock(dir); + return err; +} + +/* ---------------------------------------------------------------------- */ + +struct au_link_args { + aufs_bindex_t bdst, bsrc; + struct au_pin pin; + struct path h_path; + struct dentry *src_parent, *parent; +}; + +static int au_cpup_before_link(struct dentry *src_dentry, + struct au_link_args *a) +{ + int err; + struct dentry *h_src_dentry; + struct au_cp_generic cpg = { + .dentry = src_dentry, + .bdst = a->bdst, + .bsrc = a->bsrc, + .len = -1, + .pin = &a->pin, + .flags = AuCpup_DTIME | AuCpup_HOPEN /* | AuCpup_KEEPLINO */ + }; + + di_read_lock_parent(a->src_parent, AuLock_IR); + err = au_test_and_cpup_dirs(src_dentry, a->bdst); + if (unlikely(err)) + goto out; + + h_src_dentry = au_h_dptr(src_dentry, a->bsrc); + err = au_pin(&a->pin, src_dentry, a->bdst, + au_opt_udba(src_dentry->d_sb), + AuPin_DI_LOCKED | AuPin_MNT_WRITE); + if (unlikely(err)) + goto out; + + err = au_sio_cpup_simple(&cpg); + au_unpin(&a->pin); + +out: + di_read_unlock(a->src_parent, AuLock_IR); + return err; +} + +static int au_cpup_or_link(struct dentry *src_dentry, struct dentry *dentry, + struct au_link_args *a) +{ + int err; + unsigned char plink; + aufs_bindex_t bbot; + struct dentry *h_src_dentry; + struct inode *h_inode, *inode, *delegated; + struct super_block *sb; + struct file *h_file; + + plink = 0; + h_inode = NULL; + sb = src_dentry->d_sb; + inode = d_inode(src_dentry); + if (au_ibtop(inode) <= a->bdst) + h_inode = au_h_iptr(inode, a->bdst); + if (!h_inode || !h_inode->i_nlink) { + /* copyup src_dentry as the name of dentry. */ + bbot = au_dbbot(dentry); + if (bbot < a->bsrc) + au_set_dbbot(dentry, a->bsrc); + au_set_h_dptr(dentry, a->bsrc, + dget(au_h_dptr(src_dentry, a->bsrc))); + dget(a->h_path.dentry); + au_set_h_dptr(dentry, a->bdst, NULL); + AuDbg("temporary d_inode...\n"); + spin_lock(&dentry->d_lock); + dentry->d_inode = d_inode(src_dentry); /* tmp */ + spin_unlock(&dentry->d_lock); + h_file = au_h_open_pre(dentry, a->bsrc, /*force_wr*/0); + if (IS_ERR(h_file)) + err = PTR_ERR(h_file); + else { + struct au_cp_generic cpg = { + .dentry = dentry, + .bdst = a->bdst, + .bsrc = -1, + .len = -1, + .pin = &a->pin, + .flags = AuCpup_KEEPLINO + }; + err = au_sio_cpup_simple(&cpg); + au_h_open_post(dentry, a->bsrc, h_file); + if (!err) { + dput(a->h_path.dentry); + a->h_path.dentry = au_h_dptr(dentry, a->bdst); + } else + au_set_h_dptr(dentry, a->bdst, + a->h_path.dentry); + } + spin_lock(&dentry->d_lock); + dentry->d_inode = NULL; /* restore */ + spin_unlock(&dentry->d_lock); + AuDbg("temporary d_inode...done\n"); + au_set_h_dptr(dentry, a->bsrc, NULL); + au_set_dbbot(dentry, bbot); + } else { + /* the inode of src_dentry already exists on a.bdst branch */ + h_src_dentry = d_find_alias(h_inode); + if (!h_src_dentry && au_plink_test(inode)) { + plink = 1; + h_src_dentry = au_plink_lkup(inode, a->bdst); + err = PTR_ERR(h_src_dentry); + if (IS_ERR(h_src_dentry)) + goto out; + + if (unlikely(d_is_negative(h_src_dentry))) { + dput(h_src_dentry); + h_src_dentry = NULL; + } + + } + if (h_src_dentry) { + delegated = NULL; + err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), + &a->h_path, &delegated); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal link\n"); + iput(delegated); + } + dput(h_src_dentry); + } else { + AuIOErr("no dentry found for hi%lu on b%d\n", + h_inode->i_ino, a->bdst); + err = -EIO; + } + } + + if (!err && !plink) + au_plink_append(inode, a->bdst, a->h_path.dentry); + +out: + AuTraceErr(err); + return err; +} + +int aufs_link(struct dentry *src_dentry, struct inode *dir, + struct dentry *dentry) +{ + int err, rerr; + struct au_dtime dt; + struct au_link_args *a; + struct dentry *wh_dentry, *h_src_dentry; + struct inode *inode, *delegated; + struct super_block *sb; + struct au_wr_dir_args wr_dir_args = { + /* .force_btgt = -1, */ + .flags = AuWrDir_ADD_ENTRY + }; + + IMustLock(dir); + inode = d_inode(src_dentry); + IMustLock(inode); + + err = -ENOMEM; + a = kzalloc(sizeof(*a), GFP_NOFS); + if (unlikely(!a)) + goto out; + + a->parent = dentry->d_parent; /* dir inode is locked */ + err = aufs_read_and_write_lock2(dentry, src_dentry, + AuLock_NOPLM | AuLock_GEN); + if (unlikely(err)) + goto out_kfree; + err = au_d_linkable(src_dentry); + if (unlikely(err)) + goto out_unlock; + err = au_d_may_add(dentry); + if (unlikely(err)) + goto out_unlock; + + a->src_parent = dget_parent(src_dentry); + wr_dir_args.force_btgt = au_ibtop(inode); + + di_write_lock_parent(a->parent); + wr_dir_args.force_btgt = au_wbr(dentry, wr_dir_args.force_btgt); + wh_dentry = lock_hdir_lkup_wh(dentry, &dt, src_dentry, &a->pin, + &wr_dir_args); + err = PTR_ERR(wh_dentry); + if (IS_ERR(wh_dentry)) + goto out_parent; + + err = 0; + sb = dentry->d_sb; + a->bdst = au_dbtop(dentry); + a->h_path.dentry = au_h_dptr(dentry, a->bdst); + a->h_path.mnt = au_sbr_mnt(sb, a->bdst); + a->bsrc = au_ibtop(inode); + h_src_dentry = au_h_d_alias(src_dentry, a->bsrc); + if (!h_src_dentry && au_di(src_dentry)->di_tmpfile) + h_src_dentry = dget(au_hi_wh(inode, a->bsrc)); + if (!h_src_dentry) { + a->bsrc = au_dbtop(src_dentry); + h_src_dentry = au_h_d_alias(src_dentry, a->bsrc); + AuDebugOn(!h_src_dentry); + } else if (IS_ERR(h_src_dentry)) { + err = PTR_ERR(h_src_dentry); + goto out_parent; + } + + /* + * aufs doesn't touch the credential so + * security_dentry_create_files_as() is unnecessary. + */ + if (au_opt_test(au_mntflags(sb), PLINK)) { + if (a->bdst < a->bsrc + /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) + err = au_cpup_or_link(src_dentry, dentry, a); + else { + delegated = NULL; + err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), + &a->h_path, &delegated); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal link\n"); + iput(delegated); + } + } + dput(h_src_dentry); + } else { + /* + * copyup src_dentry to the branch we process, + * and then link(2) to it. + */ + dput(h_src_dentry); + if (a->bdst < a->bsrc + /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) { + au_unpin(&a->pin); + di_write_unlock(a->parent); + err = au_cpup_before_link(src_dentry, a); + di_write_lock_parent(a->parent); + if (!err) + err = au_pin(&a->pin, dentry, a->bdst, + au_opt_udba(sb), + AuPin_DI_LOCKED | AuPin_MNT_WRITE); + if (unlikely(err)) + goto out_wh; + } + if (!err) { + h_src_dentry = au_h_dptr(src_dentry, a->bdst); + err = -ENOENT; + if (h_src_dentry && d_is_positive(h_src_dentry)) { + delegated = NULL; + err = vfsub_link(h_src_dentry, + au_pinned_h_dir(&a->pin), + &a->h_path, &delegated); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry" + " for NFSv4 delegation" + " for an internal link\n"); + iput(delegated); + } + } + } + } + if (unlikely(err)) + goto out_unpin; + + if (wh_dentry) { + a->h_path.dentry = wh_dentry; + err = au_wh_unlink_dentry(au_pinned_h_dir(&a->pin), &a->h_path, + dentry); + if (unlikely(err)) + goto out_revert; + } + + au_dir_ts(dir, a->bdst); + inode_inc_iversion(dir); + inc_nlink(inode); + inode->i_ctime = dir->i_ctime; + d_instantiate(dentry, au_igrab(inode)); + if (d_unhashed(a->h_path.dentry)) + /* some filesystem calls d_drop() */ + d_drop(dentry); + /* some filesystems consume an inode even hardlink */ + au_fhsm_wrote(sb, a->bdst, /*force*/0); + goto out_unpin; /* success */ + +out_revert: + /* no delegation since it is just created */ + rerr = vfsub_unlink(au_pinned_h_dir(&a->pin), &a->h_path, + /*delegated*/NULL, /*force*/0); + if (unlikely(rerr)) { + AuIOErr("%pd reverting failed(%d, %d)\n", dentry, err, rerr); + err = -EIO; + } + au_dtime_revert(&dt); +out_unpin: + au_unpin(&a->pin); +out_wh: + dput(wh_dentry); +out_parent: + di_write_unlock(a->parent); + dput(a->src_parent); +out_unlock: + if (unlikely(err)) { + au_update_dbtop(dentry); + d_drop(dentry); + } + aufs_read_and_write_unlock2(dentry, src_dentry); +out_kfree: + au_kfree_rcu(a); +out: + AuTraceErr(err); + return err; +} + +int aufs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + int err, rerr; + aufs_bindex_t bindex; + unsigned char diropq; + struct path h_path; + struct dentry *wh_dentry, *parent, *opq_dentry; + struct inode *h_inode; + struct super_block *sb; + struct { + struct au_pin pin; + struct au_dtime dt; + } *a; /* reduce the stack usage */ + struct au_wr_dir_args wr_dir_args = { + .force_btgt = -1, + .flags = AuWrDir_ADD_ENTRY | AuWrDir_ISDIR + }; + + IMustLock(dir); + + err = -ENOMEM; + a = kmalloc(sizeof(*a), GFP_NOFS); + if (unlikely(!a)) + goto out; + + err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); + if (unlikely(err)) + goto out_free; + err = au_d_may_add(dentry); + if (unlikely(err)) + goto out_unlock; + + parent = dentry->d_parent; /* dir inode is locked */ + di_write_lock_parent(parent); + wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL, + &a->pin, &wr_dir_args); + err = PTR_ERR(wh_dentry); + if (IS_ERR(wh_dentry)) + goto out_parent; + + sb = dentry->d_sb; + bindex = au_dbtop(dentry); + h_path.dentry = au_h_dptr(dentry, bindex); + h_path.mnt = au_sbr_mnt(sb, bindex); + err = vfsub_mkdir(au_pinned_h_dir(&a->pin), &h_path, mode); + if (unlikely(err)) + goto out_unpin; + + /* make the dir opaque */ + diropq = 0; + h_inode = d_inode(h_path.dentry); + if (wh_dentry + || au_opt_test(au_mntflags(sb), ALWAYS_DIROPQ)) { + inode_lock_nested(h_inode, AuLsc_I_CHILD); + opq_dentry = au_diropq_create(dentry, bindex); + inode_unlock(h_inode); + err = PTR_ERR(opq_dentry); + if (IS_ERR(opq_dentry)) + goto out_dir; + dput(opq_dentry); + diropq = 1; + } + + err = epilog(dir, bindex, wh_dentry, dentry); + if (!err) { + inc_nlink(dir); + goto out_unpin; /* success */ + } + + /* revert */ + if (diropq) { + AuLabel(revert opq); + inode_lock_nested(h_inode, AuLsc_I_CHILD); + rerr = au_diropq_remove(dentry, bindex); + inode_unlock(h_inode); + if (rerr) { + AuIOErr("%pd reverting diropq failed(%d, %d)\n", + dentry, err, rerr); + err = -EIO; + } + } + +out_dir: + AuLabel(revert dir); + rerr = vfsub_rmdir(au_pinned_h_dir(&a->pin), &h_path); + if (rerr) { + AuIOErr("%pd reverting dir failed(%d, %d)\n", + dentry, err, rerr); + err = -EIO; + } + au_dtime_revert(&a->dt); +out_unpin: + au_unpin(&a->pin); + dput(wh_dentry); +out_parent: + di_write_unlock(parent); +out_unlock: + if (unlikely(err)) { + au_update_dbtop(dentry); + d_drop(dentry); + } + aufs_read_unlock(dentry, AuLock_DW); +out_free: + au_kfree_rcu(a); +out: + return err; +} diff --git a/fs/aufs/i_op_del.c b/fs/aufs/i_op_del.c new file mode 100644 index 0000000000000..bfcd75b22ea3a --- /dev/null +++ b/fs/aufs/i_op_del.c @@ -0,0 +1,520 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * inode operations (del entry) + */ + +#include +#include "aufs.h" + +/* + * decide if a new whiteout for @dentry is necessary or not. + * when it is necessary, prepare the parent dir for the upper branch whose + * branch index is @bcpup for creation. the actual creation of the whiteout will + * be done by caller. + * return value: + * 0: wh is unnecessary + * plus: wh is necessary + * minus: error + */ +int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup) +{ + int need_wh, err; + aufs_bindex_t btop; + struct super_block *sb; + + sb = dentry->d_sb; + btop = au_dbtop(dentry); + if (*bcpup < 0) { + *bcpup = btop; + if (au_test_ro(sb, btop, d_inode(dentry))) { + err = AuWbrCopyup(au_sbi(sb), dentry); + *bcpup = err; + if (unlikely(err < 0)) + goto out; + } + } else + AuDebugOn(btop < *bcpup + || au_test_ro(sb, *bcpup, d_inode(dentry))); + AuDbg("bcpup %d, btop %d\n", *bcpup, btop); + + if (*bcpup != btop) { + err = au_cpup_dirs(dentry, *bcpup); + if (unlikely(err)) + goto out; + need_wh = 1; + } else { + struct au_dinfo *dinfo, *tmp; + + need_wh = -ENOMEM; + dinfo = au_di(dentry); + tmp = au_di_alloc(sb, AuLsc_DI_TMP); + if (tmp) { + au_di_cp(tmp, dinfo); + au_di_swap(tmp, dinfo); + /* returns the number of positive dentries */ + need_wh = au_lkup_dentry(dentry, btop + 1, + /* AuLkup_IGNORE_PERM */ 0); + au_di_swap(tmp, dinfo); + au_rw_write_unlock(&tmp->di_rwsem); + au_di_free(tmp); + } + } + AuDbg("need_wh %d\n", need_wh); + err = need_wh; + +out: + return err; +} + +/* + * simple tests for the del-entry operations. + * following the checks in vfs, plus the parent-child relationship. + */ +int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, + struct dentry *h_parent, int isdir) +{ + int err; + umode_t h_mode; + struct dentry *h_dentry, *h_latest; + struct inode *h_inode; + struct path h_ppath; + struct super_block *sb; + struct au_branch *br; + + h_dentry = au_h_dptr(dentry, bindex); + if (d_really_is_positive(dentry)) { + err = -ENOENT; + if (unlikely(d_is_negative(h_dentry))) + goto out; + h_inode = d_inode(h_dentry); + if (unlikely(!h_inode->i_nlink)) + goto out; + + h_mode = h_inode->i_mode; + if (!isdir) { + err = -EISDIR; + if (unlikely(S_ISDIR(h_mode))) + goto out; + } else if (unlikely(!S_ISDIR(h_mode))) { + err = -ENOTDIR; + goto out; + } + } else { + /* rename(2) case */ + err = -EIO; + if (unlikely(d_is_positive(h_dentry))) + goto out; + } + + err = -ENOENT; + /* expected parent dir is locked */ + if (unlikely(h_parent != h_dentry->d_parent)) + goto out; + err = 0; + + /* + * rmdir a dir may break the consistency on some filesystem. + * let's try heavy test. + */ + err = -EACCES; + sb = dentry->d_sb; + br = au_sbr(sb, bindex); + if (unlikely(!au_opt_test(au_mntflags(sb), DIRPERM1) + && au_test_h_perm(d_inode(h_parent), + MAY_EXEC | MAY_WRITE))) + goto out; + + h_ppath.dentry = h_parent; + h_ppath.mnt = au_br_mnt(br); + h_latest = au_sio_lkup_one(&dentry->d_name, &h_ppath); + err = -EIO; + if (IS_ERR(h_latest)) + goto out; + if (h_latest == h_dentry) + err = 0; + dput(h_latest); + +out: + return err; +} + +/* + * decide the branch where we operate for @dentry. the branch index will be set + * @rbcpup. after deciding it, 'pin' it and store the timestamps of the parent + * dir for reverting. + * when a new whiteout is necessary, create it. + */ +static struct dentry* +lock_hdir_create_wh(struct dentry *dentry, int isdir, aufs_bindex_t *rbcpup, + struct au_dtime *dt, struct au_pin *pin) +{ + struct dentry *wh_dentry; + struct super_block *sb; + struct path h_path; + int err, need_wh; + unsigned int udba; + aufs_bindex_t bcpup; + + need_wh = au_wr_dir_need_wh(dentry, isdir, rbcpup); + wh_dentry = ERR_PTR(need_wh); + if (unlikely(need_wh < 0)) + goto out; + + sb = dentry->d_sb; + udba = au_opt_udba(sb); + bcpup = *rbcpup; + err = au_pin(pin, dentry, bcpup, udba, + AuPin_DI_LOCKED | AuPin_MNT_WRITE); + wh_dentry = ERR_PTR(err); + if (unlikely(err)) + goto out; + + h_path.dentry = au_pinned_h_parent(pin); + if (udba != AuOpt_UDBA_NONE + && au_dbtop(dentry) == bcpup) { + err = au_may_del(dentry, bcpup, h_path.dentry, isdir); + wh_dentry = ERR_PTR(err); + if (unlikely(err)) + goto out_unpin; + } + + h_path.mnt = au_sbr_mnt(sb, bcpup); + au_dtime_store(dt, au_pinned_parent(pin), &h_path); + wh_dentry = NULL; + if (!need_wh) + goto out; /* success, no need to create whiteout */ + + wh_dentry = au_wh_create(dentry, bcpup, h_path.dentry); + if (IS_ERR(wh_dentry)) + goto out_unpin; + + /* returns with the parent is locked and wh_dentry is dget-ed */ + goto out; /* success */ + +out_unpin: + au_unpin(pin); +out: + return wh_dentry; +} + +/* + * when removing a dir, rename it to a unique temporary whiteout-ed name first + * in order to be revertible and save time for removing many child whiteouts + * under the dir. + * returns 1 when there are too many child whiteout and caller should remove + * them asynchronously. returns 0 when the number of children is enough small to + * remove now or the branch fs is a remote fs. + * otherwise return an error. + */ +static int renwh_and_rmdir(struct dentry *dentry, aufs_bindex_t bindex, + struct au_nhash *whlist, struct inode *dir) +{ + int rmdir_later, err, dirwh; + struct dentry *h_dentry; + struct super_block *sb; + struct inode *inode; + + sb = dentry->d_sb; + SiMustAnyLock(sb); + h_dentry = au_h_dptr(dentry, bindex); + err = au_whtmp_ren(h_dentry, au_sbr(sb, bindex)); + if (unlikely(err)) + goto out; + + /* stop monitoring */ + inode = d_inode(dentry); + au_hn_free(au_hi(inode, bindex)); + + if (!au_test_fs_remote(h_dentry->d_sb)) { + dirwh = au_sbi(sb)->si_dirwh; + rmdir_later = (dirwh <= 1); + if (!rmdir_later) + rmdir_later = au_nhash_test_longer_wh(whlist, bindex, + dirwh); + if (rmdir_later) + return rmdir_later; + } + + err = au_whtmp_rmdir(dir, bindex, h_dentry, whlist); + if (unlikely(err)) { + AuIOErr("rmdir %pd, b%d failed, %d. ignored\n", + h_dentry, bindex, err); + err = 0; + } + +out: + AuTraceErr(err); + return err; +} + +/* + * final procedure for deleting a entry. + * maintain dentry and iattr. + */ +static void epilog(struct inode *dir, struct dentry *dentry, + aufs_bindex_t bindex) +{ + struct inode *inode; + + inode = d_inode(dentry); + d_drop(dentry); + inode->i_ctime = dir->i_ctime; + + au_dir_ts(dir, bindex); + inode_inc_iversion(dir); +} + +/* + * when an error happened, remove the created whiteout and revert everything. + */ +static int do_revert(int err, struct inode *dir, aufs_bindex_t bindex, + aufs_bindex_t bwh, struct dentry *wh_dentry, + struct dentry *dentry, struct au_dtime *dt) +{ + int rerr; + struct path h_path = { + .dentry = wh_dentry, + .mnt = au_sbr_mnt(dir->i_sb, bindex) + }; + + rerr = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, dentry); + if (!rerr) { + au_set_dbwh(dentry, bwh); + au_dtime_revert(dt); + return 0; + } + + AuIOErr("%pd reverting whiteout failed(%d, %d)\n", dentry, err, rerr); + return -EIO; +} + +/* ---------------------------------------------------------------------- */ + +int aufs_unlink(struct inode *dir, struct dentry *dentry) +{ + int err; + aufs_bindex_t bwh, bindex, btop; + struct inode *inode, *h_dir, *delegated; + struct dentry *parent, *wh_dentry; + /* to reduce stack size */ + struct { + struct au_dtime dt; + struct au_pin pin; + struct path h_path; + } *a; + + IMustLock(dir); + + err = -ENOMEM; + a = kmalloc(sizeof(*a), GFP_NOFS); + if (unlikely(!a)) + goto out; + + err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); + if (unlikely(err)) + goto out_free; + err = au_d_hashed_positive(dentry); + if (unlikely(err)) + goto out_unlock; + inode = d_inode(dentry); + IMustLock(inode); + err = -EISDIR; + if (unlikely(d_is_dir(dentry))) + goto out_unlock; /* possible? */ + + btop = au_dbtop(dentry); + bwh = au_dbwh(dentry); + bindex = -1; + parent = dentry->d_parent; /* dir inode is locked */ + di_write_lock_parent(parent); + wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &a->dt, + &a->pin); + err = PTR_ERR(wh_dentry); + if (IS_ERR(wh_dentry)) + goto out_parent; + + a->h_path.mnt = au_sbr_mnt(dentry->d_sb, btop); + a->h_path.dentry = au_h_dptr(dentry, btop); + dget(a->h_path.dentry); + if (bindex == btop) { + h_dir = au_pinned_h_dir(&a->pin); + delegated = NULL; + err = vfsub_unlink(h_dir, &a->h_path, &delegated, /*force*/0); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal unlink\n"); + iput(delegated); + } + } else { + /* dir inode is locked */ + h_dir = d_inode(wh_dentry->d_parent); + IMustLock(h_dir); + err = 0; + } + + if (!err) { + vfsub_drop_nlink(inode); + epilog(dir, dentry, bindex); + + /* update target timestamps */ + if (bindex == btop) { + vfsub_update_h_iattr(&a->h_path, /*did*/NULL); + /*ignore*/ + inode->i_ctime = d_inode(a->h_path.dentry)->i_ctime; + } else + /* todo: this timestamp may be reverted later */ + inode->i_ctime = h_dir->i_ctime; + goto out_unpin; /* success */ + } + + /* revert */ + if (wh_dentry) { + int rerr; + + rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, + &a->dt); + if (rerr) + err = rerr; + } + +out_unpin: + au_unpin(&a->pin); + dput(wh_dentry); + dput(a->h_path.dentry); +out_parent: + di_write_unlock(parent); +out_unlock: + aufs_read_unlock(dentry, AuLock_DW); +out_free: + au_kfree_rcu(a); +out: + return err; +} + +int aufs_rmdir(struct inode *dir, struct dentry *dentry) +{ + int err, rmdir_later; + aufs_bindex_t bwh, bindex, btop; + struct inode *inode; + struct dentry *parent, *wh_dentry, *h_dentry; + struct au_whtmp_rmdir *args; + /* to reduce stack size */ + struct { + struct au_dtime dt; + struct au_pin pin; + } *a; + + IMustLock(dir); + + err = -ENOMEM; + a = kmalloc(sizeof(*a), GFP_NOFS); + if (unlikely(!a)) + goto out; + + err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN); + if (unlikely(err)) + goto out_free; + err = au_alive_dir(dentry); + if (unlikely(err)) + goto out_unlock; + inode = d_inode(dentry); + IMustLock(inode); + err = -ENOTDIR; + if (unlikely(!d_is_dir(dentry))) + goto out_unlock; /* possible? */ + + err = -ENOMEM; + args = au_whtmp_rmdir_alloc(dir->i_sb, GFP_NOFS); + if (unlikely(!args)) + goto out_unlock; + + parent = dentry->d_parent; /* dir inode is locked */ + di_write_lock_parent(parent); + err = au_test_empty(dentry, &args->whlist); + if (unlikely(err)) + goto out_parent; + + btop = au_dbtop(dentry); + bwh = au_dbwh(dentry); + bindex = -1; + wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &a->dt, + &a->pin); + err = PTR_ERR(wh_dentry); + if (IS_ERR(wh_dentry)) + goto out_parent; + + h_dentry = au_h_dptr(dentry, btop); + dget(h_dentry); + rmdir_later = 0; + if (bindex == btop) { + err = renwh_and_rmdir(dentry, btop, &args->whlist, dir); + if (err > 0) { + rmdir_later = err; + err = 0; + } + } else { + /* stop monitoring */ + au_hn_free(au_hi(inode, btop)); + + /* dir inode is locked */ + IMustLock(d_inode(wh_dentry->d_parent)); + err = 0; + } + + if (!err) { + vfsub_dead_dir(inode); + au_set_dbdiropq(dentry, -1); + epilog(dir, dentry, bindex); + + if (rmdir_later) { + au_whtmp_kick_rmdir(dir, btop, h_dentry, args); + args = NULL; + } + + goto out_unpin; /* success */ + } + + /* revert */ + AuLabel(revert); + if (wh_dentry) { + int rerr; + + rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, + &a->dt); + if (rerr) + err = rerr; + } + +out_unpin: + au_unpin(&a->pin); + dput(wh_dentry); + dput(h_dentry); +out_parent: + di_write_unlock(parent); + if (args) + au_whtmp_rmdir_free(args); +out_unlock: + aufs_read_unlock(dentry, AuLock_DW); +out_free: + au_kfree_rcu(a); +out: + AuTraceErr(err); + return err; +} diff --git a/fs/aufs/i_op_ren.c b/fs/aufs/i_op_ren.c new file mode 100644 index 0000000000000..59825a53b1e95 --- /dev/null +++ b/fs/aufs/i_op_ren.c @@ -0,0 +1,1256 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * inode operation (rename entry) + * todo: this is crazy monster + */ + +#include +#include "aufs.h" + +enum { AuSRC, AuDST, AuSrcDst }; +enum { AuPARENT, AuCHILD, AuParentChild }; + +#define AuRen_ISDIR_SRC 1 +#define AuRen_ISDIR_DST (1 << 1) +#define AuRen_ISSAMEDIR (1 << 2) +#define AuRen_WHSRC (1 << 3) +#define AuRen_WHDST (1 << 4) +#define AuRen_MNT_WRITE (1 << 5) +#define AuRen_DT_DSTDIR (1 << 6) +#define AuRen_DIROPQ_SRC (1 << 7) +#define AuRen_DIROPQ_DST (1 << 8) +#define AuRen_DIRREN (1 << 9) +#define AuRen_DROPPED_SRC (1 << 10) +#define AuRen_DROPPED_DST (1 << 11) +#define au_ftest_ren(flags, name) ((flags) & AuRen_##name) +#define au_fset_ren(flags, name) \ + do { (flags) |= AuRen_##name; } while (0) +#define au_fclr_ren(flags, name) \ + do { (flags) &= ~AuRen_##name; } while (0) + +#ifndef CONFIG_AUFS_DIRREN +#undef AuRen_DIRREN +#define AuRen_DIRREN 0 +#endif + +struct au_ren_args { + struct { + struct dentry *dentry, *h_dentry, *parent, *h_parent, + *wh_dentry; + struct inode *dir, *inode; + struct au_hinode *hdir, *hinode; + struct au_dtime dt[AuParentChild]; + aufs_bindex_t btop, bdiropq; + } sd[AuSrcDst]; + +#define src_dentry sd[AuSRC].dentry +#define src_dir sd[AuSRC].dir +#define src_inode sd[AuSRC].inode +#define src_h_dentry sd[AuSRC].h_dentry +#define src_parent sd[AuSRC].parent +#define src_h_parent sd[AuSRC].h_parent +#define src_wh_dentry sd[AuSRC].wh_dentry +#define src_hdir sd[AuSRC].hdir +#define src_hinode sd[AuSRC].hinode +#define src_h_dir sd[AuSRC].hdir->hi_inode +#define src_dt sd[AuSRC].dt +#define src_btop sd[AuSRC].btop +#define src_bdiropq sd[AuSRC].bdiropq + +#define dst_dentry sd[AuDST].dentry +#define dst_dir sd[AuDST].dir +#define dst_inode sd[AuDST].inode +#define dst_h_dentry sd[AuDST].h_dentry +#define dst_parent sd[AuDST].parent +#define dst_h_parent sd[AuDST].h_parent +#define dst_wh_dentry sd[AuDST].wh_dentry +#define dst_hdir sd[AuDST].hdir +#define dst_hinode sd[AuDST].hinode +#define dst_h_dir sd[AuDST].hdir->hi_inode +#define dst_dt sd[AuDST].dt +#define dst_btop sd[AuDST].btop +#define dst_bdiropq sd[AuDST].bdiropq + + struct dentry *h_trap; + struct au_branch *br; + struct path h_path; + struct au_nhash whlist; + aufs_bindex_t btgt, src_bwh; + + struct { + unsigned short auren_flags; + unsigned char flags; /* syscall parameter */ + unsigned char exchange; + } __packed; + + struct au_whtmp_rmdir *thargs; + struct dentry *h_dst; + struct au_hinode *h_root; +}; + +/* ---------------------------------------------------------------------- */ + +/* + * functions for reverting. + * when an error happened in a single rename systemcall, we should revert + * everything as if nothing happened. + * we don't need to revert the copied-up/down the parent dir since they are + * harmless. + */ + +#define RevertFailure(fmt, ...) do { \ + AuIOErr("revert failure: " fmt " (%d, %d)\n", \ + ##__VA_ARGS__, err, rerr); \ + err = -EIO; \ +} while (0) + +static void au_ren_do_rev_diropq(int err, struct au_ren_args *a, int idx) +{ + int rerr; + struct dentry *d; +#define src_or_dst(member) a->sd[idx].member + + d = src_or_dst(dentry); /* {src,dst}_dentry */ + au_hn_inode_lock_nested(src_or_dst(hinode), AuLsc_I_CHILD); + rerr = au_diropq_remove(d, a->btgt); + au_hn_inode_unlock(src_or_dst(hinode)); + au_set_dbdiropq(d, src_or_dst(bdiropq)); + if (rerr) + RevertFailure("remove diropq %pd", d); + +#undef src_or_dst_ +} + +static void au_ren_rev_diropq(int err, struct au_ren_args *a) +{ + if (au_ftest_ren(a->auren_flags, DIROPQ_SRC)) + au_ren_do_rev_diropq(err, a, AuSRC); + if (au_ftest_ren(a->auren_flags, DIROPQ_DST)) + au_ren_do_rev_diropq(err, a, AuDST); +} + +static void au_ren_rev_rename(int err, struct au_ren_args *a) +{ + int rerr; + struct inode *delegated; + struct path h_ppath = { + .dentry = a->src_h_parent, + .mnt = a->h_path.mnt + }; + + a->h_path.dentry = vfsub_lkup_one(&a->src_dentry->d_name, &h_ppath); + rerr = PTR_ERR(a->h_path.dentry); + if (IS_ERR(a->h_path.dentry)) { + RevertFailure("lkup one %pd", a->src_dentry); + return; + } + + delegated = NULL; + rerr = vfsub_rename(a->dst_h_dir, + au_h_dptr(a->src_dentry, a->btgt), + a->src_h_dir, &a->h_path, &delegated, a->flags); + if (unlikely(rerr == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal rename\n"); + iput(delegated); + } + d_drop(a->h_path.dentry); + dput(a->h_path.dentry); + /* au_set_h_dptr(a->src_dentry, a->btgt, NULL); */ + if (rerr) + RevertFailure("rename %pd", a->src_dentry); +} + +static void au_ren_rev_whtmp(int err, struct au_ren_args *a) +{ + int rerr; + struct inode *delegated; + struct path h_ppath = { + .dentry = a->dst_h_parent, + .mnt = a->h_path.mnt + }; + + a->h_path.dentry = vfsub_lkup_one(&a->dst_dentry->d_name, &h_ppath); + rerr = PTR_ERR(a->h_path.dentry); + if (IS_ERR(a->h_path.dentry)) { + RevertFailure("lkup one %pd", a->dst_dentry); + return; + } + if (d_is_positive(a->h_path.dentry)) { + d_drop(a->h_path.dentry); + dput(a->h_path.dentry); + return; + } + + delegated = NULL; + rerr = vfsub_rename(a->dst_h_dir, a->h_dst, a->dst_h_dir, &a->h_path, + &delegated, a->flags); + if (unlikely(rerr == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal rename\n"); + iput(delegated); + } + d_drop(a->h_path.dentry); + dput(a->h_path.dentry); + if (!rerr) + au_set_h_dptr(a->dst_dentry, a->btgt, dget(a->h_dst)); + else + RevertFailure("rename %pd", a->h_dst); +} + +static void au_ren_rev_whsrc(int err, struct au_ren_args *a) +{ + int rerr; + + a->h_path.dentry = a->src_wh_dentry; + rerr = au_wh_unlink_dentry(a->src_h_dir, &a->h_path, a->src_dentry); + au_set_dbwh(a->src_dentry, a->src_bwh); + if (rerr) + RevertFailure("unlink %pd", a->src_wh_dentry); +} +#undef RevertFailure + +/* ---------------------------------------------------------------------- */ + +/* + * when we have to copyup the renaming entry, do it with the rename-target name + * in order to minimize the cost (the later actual rename is unnecessary). + * otherwise rename it on the target branch. + */ +static int au_ren_or_cpup(struct au_ren_args *a) +{ + int err; + struct dentry *d; + struct inode *delegated; + + d = a->src_dentry; + if (au_dbtop(d) == a->btgt) { + a->h_path.dentry = a->dst_h_dentry; + AuDebugOn(au_dbtop(d) != a->btgt); + delegated = NULL; + err = vfsub_rename(a->src_h_dir, au_h_dptr(d, a->btgt), + a->dst_h_dir, &a->h_path, &delegated, + a->flags); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal rename\n"); + iput(delegated); + } + } else + BUG(); + + if (!err && a->h_dst) + /* it will be set to dinfo later */ + dget(a->h_dst); + + return err; +} + +/* cf. aufs_rmdir() */ +static int au_ren_del_whtmp(struct au_ren_args *a) +{ + int err; + struct inode *dir; + + dir = a->dst_dir; + SiMustAnyLock(dir->i_sb); + if (!au_nhash_test_longer_wh(&a->whlist, a->btgt, + au_sbi(dir->i_sb)->si_dirwh) + || au_test_fs_remote(a->h_dst->d_sb)) { + err = au_whtmp_rmdir(dir, a->btgt, a->h_dst, &a->whlist); + if (unlikely(err)) + pr_warn("failed removing whtmp dir %pd (%d), " + "ignored.\n", a->h_dst, err); + } else { + au_nhash_wh_free(&a->thargs->whlist); + a->thargs->whlist = a->whlist; + a->whlist.nh_num = 0; + au_whtmp_kick_rmdir(dir, a->btgt, a->h_dst, a->thargs); + dput(a->h_dst); + a->thargs = NULL; + } + + return 0; +} + +/* make it 'opaque' dir. */ +static int au_ren_do_diropq(struct au_ren_args *a, int idx) +{ + int err; + struct dentry *d, *diropq; +#define src_or_dst(member) a->sd[idx].member + + err = 0; + d = src_or_dst(dentry); /* {src,dst}_dentry */ + src_or_dst(bdiropq) = au_dbdiropq(d); + src_or_dst(hinode) = au_hi(src_or_dst(inode), a->btgt); + au_hn_inode_lock_nested(src_or_dst(hinode), AuLsc_I_CHILD); + diropq = au_diropq_create(d, a->btgt); + au_hn_inode_unlock(src_or_dst(hinode)); + if (IS_ERR(diropq)) + err = PTR_ERR(diropq); + else + dput(diropq); + +#undef src_or_dst_ + return err; +} + +static int au_ren_diropq(struct au_ren_args *a) +{ + int err; + unsigned char always; + struct dentry *d; + + err = 0; + d = a->dst_dentry; /* already renamed on the branch */ + always = !!au_opt_test(au_mntflags(d->d_sb), ALWAYS_DIROPQ); + if (au_ftest_ren(a->auren_flags, ISDIR_SRC) + && !au_ftest_ren(a->auren_flags, DIRREN) + && a->btgt != au_dbdiropq(a->src_dentry) + && (a->dst_wh_dentry + || a->btgt <= au_dbdiropq(d) + /* hide the lower to keep xino */ + /* the lowers may not be a dir, but we hide them anyway */ + || a->btgt < au_dbbot(d) + || always)) { + AuDbg("here\n"); + err = au_ren_do_diropq(a, AuSRC); + if (unlikely(err)) + goto out; + au_fset_ren(a->auren_flags, DIROPQ_SRC); + } + if (!a->exchange) + goto out; /* success */ + + d = a->src_dentry; /* already renamed on the branch */ + if (au_ftest_ren(a->auren_flags, ISDIR_DST) + && a->btgt != au_dbdiropq(a->dst_dentry) + && (a->btgt < au_dbdiropq(d) + || a->btgt < au_dbbot(d) + || always)) { + AuDbgDentry(a->src_dentry); + AuDbgDentry(a->dst_dentry); + err = au_ren_do_diropq(a, AuDST); + if (unlikely(err)) + goto out_rev_src; + au_fset_ren(a->auren_flags, DIROPQ_DST); + } + goto out; /* success */ + +out_rev_src: + AuDbg("err %d, reverting src\n", err); + au_ren_rev_diropq(err, a); +out: + return err; +} + +static int do_rename(struct au_ren_args *a) +{ + int err; + struct dentry *d, *h_d; + + if (!a->exchange) { + /* prepare workqueue args for asynchronous rmdir */ + h_d = a->dst_h_dentry; + if (au_ftest_ren(a->auren_flags, ISDIR_DST) + /* && !au_ftest_ren(a->auren_flags, DIRREN) */ + && d_is_positive(h_d)) { + err = -ENOMEM; + a->thargs = au_whtmp_rmdir_alloc(a->src_dentry->d_sb, + GFP_NOFS); + if (unlikely(!a->thargs)) + goto out; + a->h_dst = dget(h_d); + } + + /* create whiteout for src_dentry */ + if (au_ftest_ren(a->auren_flags, WHSRC)) { + a->src_bwh = au_dbwh(a->src_dentry); + AuDebugOn(a->src_bwh >= 0); + a->src_wh_dentry = au_wh_create(a->src_dentry, a->btgt, + a->src_h_parent); + err = PTR_ERR(a->src_wh_dentry); + if (IS_ERR(a->src_wh_dentry)) + goto out_thargs; + } + + /* lookup whiteout for dentry */ + if (au_ftest_ren(a->auren_flags, WHDST)) { + h_d = au_wh_lkup(a->dst_h_parent, + &a->dst_dentry->d_name, a->br); + err = PTR_ERR(h_d); + if (IS_ERR(h_d)) + goto out_whsrc; + if (d_is_negative(h_d)) + dput(h_d); + else + a->dst_wh_dentry = h_d; + } + + /* rename dentry to tmpwh */ + if (a->thargs) { + err = au_whtmp_ren(a->dst_h_dentry, a->br); + if (unlikely(err)) + goto out_whdst; + + d = a->dst_dentry; + au_set_h_dptr(d, a->btgt, NULL); + err = au_lkup_neg(d, a->btgt, /*wh*/0); + if (unlikely(err)) + goto out_whtmp; + a->dst_h_dentry = au_h_dptr(d, a->btgt); + } + } + + BUG_ON(d_is_positive(a->dst_h_dentry) && a->src_btop != a->btgt); +#if 0 /* debugging */ + BUG_ON(!au_ftest_ren(a->auren_flags, DIRREN) + && d_is_positive(a->dst_h_dentry) + && a->src_btop != a->btgt); +#endif + + /* rename by vfs_rename or cpup */ + err = au_ren_or_cpup(a); + if (unlikely(err)) + /* leave the copied-up one */ + goto out_whtmp; + + /* make dir opaque */ + err = au_ren_diropq(a); + if (unlikely(err)) + goto out_rename; + + /* update target timestamps */ + if (a->exchange) { + AuDebugOn(au_dbtop(a->dst_dentry) != a->btgt); + a->h_path.dentry = au_h_dptr(a->dst_dentry, a->btgt); + vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/ + a->dst_inode->i_ctime = d_inode(a->h_path.dentry)->i_ctime; + } + AuDebugOn(au_dbtop(a->src_dentry) != a->btgt); + a->h_path.dentry = au_h_dptr(a->src_dentry, a->btgt); + vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/ + a->src_inode->i_ctime = d_inode(a->h_path.dentry)->i_ctime; + + if (!a->exchange) { + /* remove whiteout for dentry */ + if (a->dst_wh_dentry) { + a->h_path.dentry = a->dst_wh_dentry; + err = au_wh_unlink_dentry(a->dst_h_dir, &a->h_path, + a->dst_dentry); + if (unlikely(err)) + goto out_diropq; + } + + /* remove whtmp */ + if (a->thargs) + au_ren_del_whtmp(a); /* ignore this error */ + + au_fhsm_wrote(a->src_dentry->d_sb, a->btgt, /*force*/0); + } + err = 0; + goto out_success; + +out_diropq: + au_ren_rev_diropq(err, a); +out_rename: + au_ren_rev_rename(err, a); + dput(a->h_dst); +out_whtmp: + if (a->thargs) + au_ren_rev_whtmp(err, a); +out_whdst: + dput(a->dst_wh_dentry); + a->dst_wh_dentry = NULL; +out_whsrc: + if (a->src_wh_dentry) + au_ren_rev_whsrc(err, a); +out_success: + dput(a->src_wh_dentry); + dput(a->dst_wh_dentry); +out_thargs: + if (a->thargs) { + dput(a->h_dst); + au_whtmp_rmdir_free(a->thargs); + a->thargs = NULL; + } +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* + * test if @dentry dir can be rename destination or not. + * success means, it is a logically empty dir. + */ +static int may_rename_dstdir(struct dentry *dentry, struct au_nhash *whlist) +{ + return au_test_empty(dentry, whlist); +} + +/* + * test if @a->src_dentry dir can be rename source or not. + * if it can, return 0. + * success means, + * - it is a logically empty dir. + * - or, it exists on writable branch and has no children including whiteouts + * on the lower branch unless DIRREN is on. + */ +static int may_rename_srcdir(struct au_ren_args *a) +{ + int err; + unsigned int rdhash; + aufs_bindex_t btop, btgt; + struct dentry *dentry; + struct super_block *sb; + struct au_sbinfo *sbinfo; + + dentry = a->src_dentry; + sb = dentry->d_sb; + sbinfo = au_sbi(sb); + if (au_opt_test(sbinfo->si_mntflags, DIRREN)) + au_fset_ren(a->auren_flags, DIRREN); + + btgt = a->btgt; + btop = au_dbtop(dentry); + if (btop != btgt) { + struct au_nhash whlist; + + SiMustAnyLock(sb); + rdhash = sbinfo->si_rdhash; + if (!rdhash) + rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, + dentry)); + err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS); + if (unlikely(err)) + goto out; + err = au_test_empty(dentry, &whlist); + au_nhash_wh_free(&whlist); + goto out; + } + + if (btop == au_dbtaildir(dentry)) + return 0; /* success */ + + err = au_test_empty_lower(dentry); + +out: + if (err == -ENOTEMPTY) { + if (au_ftest_ren(a->auren_flags, DIRREN)) { + err = 0; + } else { + AuWarn1("renaming dir who has child(ren) on multiple " + "branches, is not supported\n"); + err = -EXDEV; + } + } + return err; +} + +/* side effect: sets whlist and h_dentry */ +static int au_ren_may_dir(struct au_ren_args *a) +{ + int err; + unsigned int rdhash; + struct dentry *d; + + d = a->dst_dentry; + SiMustAnyLock(d->d_sb); + + err = 0; + if (au_ftest_ren(a->auren_flags, ISDIR_DST) && a->dst_inode) { + rdhash = au_sbi(d->d_sb)->si_rdhash; + if (!rdhash) + rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, d)); + err = au_nhash_alloc(&a->whlist, rdhash, GFP_NOFS); + if (unlikely(err)) + goto out; + + if (!a->exchange) { + au_set_dbtop(d, a->dst_btop); + err = may_rename_dstdir(d, &a->whlist); + au_set_dbtop(d, a->btgt); + } else + err = may_rename_srcdir(a); + } + a->dst_h_dentry = au_h_dptr(d, au_dbtop(d)); + if (unlikely(err)) + goto out; + + d = a->src_dentry; + a->src_h_dentry = au_h_dptr(d, au_dbtop(d)); + if (au_ftest_ren(a->auren_flags, ISDIR_SRC)) { + err = may_rename_srcdir(a); + if (unlikely(err)) { + au_nhash_wh_free(&a->whlist); + a->whlist.nh_num = 0; + } + } +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* + * simple tests for rename. + * following the checks in vfs, plus the parent-child relationship. + */ +static int au_may_ren(struct au_ren_args *a) +{ + int err, isdir; + struct inode *h_inode; + + if (a->src_btop == a->btgt) { + err = au_may_del(a->src_dentry, a->btgt, a->src_h_parent, + au_ftest_ren(a->auren_flags, ISDIR_SRC)); + if (unlikely(err)) + goto out; + err = -EINVAL; + if (unlikely(a->src_h_dentry == a->h_trap)) + goto out; + } + + err = 0; + if (a->dst_btop != a->btgt) + goto out; + + err = -ENOTEMPTY; + if (unlikely(a->dst_h_dentry == a->h_trap)) + goto out; + + err = -EIO; + isdir = !!au_ftest_ren(a->auren_flags, ISDIR_DST); + if (d_really_is_negative(a->dst_dentry)) { + if (d_is_negative(a->dst_h_dentry)) + err = au_may_add(a->dst_dentry, a->btgt, + a->dst_h_parent, isdir); + } else { + if (unlikely(d_is_negative(a->dst_h_dentry))) + goto out; + h_inode = d_inode(a->dst_h_dentry); + if (h_inode->i_nlink) + err = au_may_del(a->dst_dentry, a->btgt, + a->dst_h_parent, isdir); + } + +out: + if (unlikely(err == -ENOENT || err == -EEXIST)) + err = -EIO; + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* + * locking order + * (VFS) + * - src_dir and dir by lock_rename() + * - inode if exists + * (aufs) + * - lock all + * + src_dentry and dentry by aufs_read_and_write_lock2() which calls, + * + si_read_lock + * + di_write_lock2_child() + * + di_write_lock_child() + * + ii_write_lock_child() + * + di_write_lock_child2() + * + ii_write_lock_child2() + * + src_parent and parent + * + di_write_lock_parent() + * + ii_write_lock_parent() + * + di_write_lock_parent2() + * + ii_write_lock_parent2() + * + lower src_dir and dir by vfsub_lock_rename() + * + verify the every relationships between child and parent. if any + * of them failed, unlock all and return -EBUSY. + */ +static void au_ren_unlock(struct au_ren_args *a) +{ + vfsub_unlock_rename(a->src_h_parent, a->src_hdir, + a->dst_h_parent, a->dst_hdir); + if (au_ftest_ren(a->auren_flags, DIRREN) + && a->h_root) + au_hn_inode_unlock(a->h_root); + if (au_ftest_ren(a->auren_flags, MNT_WRITE)) + vfsub_mnt_drop_write(au_br_mnt(a->br)); +} + +static int au_ren_lock(struct au_ren_args *a) +{ + int err; + unsigned int udba; + + err = 0; + a->src_h_parent = au_h_dptr(a->src_parent, a->btgt); + a->src_hdir = au_hi(a->src_dir, a->btgt); + a->dst_h_parent = au_h_dptr(a->dst_parent, a->btgt); + a->dst_hdir = au_hi(a->dst_dir, a->btgt); + + err = vfsub_mnt_want_write(au_br_mnt(a->br)); + if (unlikely(err)) + goto out; + au_fset_ren(a->auren_flags, MNT_WRITE); + if (au_ftest_ren(a->auren_flags, DIRREN)) { + struct dentry *root; + struct inode *dir; + + /* + * sbinfo is already locked, so this ii_read_lock is + * unnecessary. but our debugging feature checks it. + */ + root = a->src_inode->i_sb->s_root; + if (root != a->src_parent && root != a->dst_parent) { + dir = d_inode(root); + ii_read_lock_parent3(dir); + a->h_root = au_hi(dir, a->btgt); + ii_read_unlock(dir); + au_hn_inode_lock_nested(a->h_root, AuLsc_I_PARENT3); + } + } + a->h_trap = vfsub_lock_rename(a->src_h_parent, a->src_hdir, + a->dst_h_parent, a->dst_hdir); + udba = au_opt_udba(a->src_dentry->d_sb); + if (unlikely(a->src_hdir->hi_inode != d_inode(a->src_h_parent) + || a->dst_hdir->hi_inode != d_inode(a->dst_h_parent))) + err = au_busy_or_stale(); + if (!err && au_dbtop(a->src_dentry) == a->btgt) + err = au_h_verify(a->src_h_dentry, udba, + d_inode(a->src_h_parent), a->src_h_parent, + a->br); + if (!err && au_dbtop(a->dst_dentry) == a->btgt) + err = au_h_verify(a->dst_h_dentry, udba, + d_inode(a->dst_h_parent), a->dst_h_parent, + a->br); + if (!err) + goto out; /* success */ + + err = au_busy_or_stale(); + au_ren_unlock(a); + +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +static void au_ren_refresh_dir(struct au_ren_args *a) +{ + struct inode *dir; + + dir = a->dst_dir; + inode_inc_iversion(dir); + if (au_ftest_ren(a->auren_flags, ISDIR_SRC)) { + /* is this updating defined in POSIX? */ + au_cpup_attr_timesizes(a->src_inode); + au_cpup_attr_nlink(dir, /*force*/1); + } + au_dir_ts(dir, a->btgt); + + if (a->exchange) { + dir = a->src_dir; + inode_inc_iversion(dir); + if (au_ftest_ren(a->auren_flags, ISDIR_DST)) { + /* is this updating defined in POSIX? */ + au_cpup_attr_timesizes(a->dst_inode); + au_cpup_attr_nlink(dir, /*force*/1); + } + au_dir_ts(dir, a->btgt); + } + + if (au_ftest_ren(a->auren_flags, ISSAMEDIR)) + return; + + dir = a->src_dir; + inode_inc_iversion(dir); + if (au_ftest_ren(a->auren_flags, ISDIR_SRC)) + au_cpup_attr_nlink(dir, /*force*/1); + au_dir_ts(dir, a->btgt); +} + +static void au_ren_refresh(struct au_ren_args *a) +{ + aufs_bindex_t bbot, bindex; + struct dentry *d, *h_d; + struct inode *i, *h_i; + struct super_block *sb; + + d = a->dst_dentry; + d_drop(d); + if (a->h_dst) + /* already dget-ed by au_ren_or_cpup() */ + au_set_h_dptr(d, a->btgt, a->h_dst); + + i = a->dst_inode; + if (i) { + if (!a->exchange) { + if (!au_ftest_ren(a->auren_flags, ISDIR_DST)) + vfsub_drop_nlink(i); + else { + vfsub_dead_dir(i); + au_cpup_attr_timesizes(i); + } + au_update_dbrange(d, /*do_put_zero*/1); + } else + au_cpup_attr_nlink(i, /*force*/1); + } else { + bbot = a->btgt; + for (bindex = au_dbtop(d); bindex < bbot; bindex++) + au_set_h_dptr(d, bindex, NULL); + bbot = au_dbbot(d); + for (bindex = a->btgt + 1; bindex <= bbot; bindex++) + au_set_h_dptr(d, bindex, NULL); + au_update_dbrange(d, /*do_put_zero*/0); + } + + if (a->exchange + || au_ftest_ren(a->auren_flags, DIRREN)) { + d_drop(a->src_dentry); + if (au_ftest_ren(a->auren_flags, DIRREN)) + au_set_dbwh(a->src_dentry, -1); + return; + } + + d = a->src_dentry; + au_set_dbwh(d, -1); + bbot = au_dbbot(d); + for (bindex = a->btgt + 1; bindex <= bbot; bindex++) { + h_d = au_h_dptr(d, bindex); + if (h_d) + au_set_h_dptr(d, bindex, NULL); + } + au_set_dbbot(d, a->btgt); + + sb = d->d_sb; + i = a->src_inode; + if (au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(i)) + return; /* success */ + + bbot = au_ibbot(i); + for (bindex = a->btgt + 1; bindex <= bbot; bindex++) { + h_i = au_h_iptr(i, bindex); + if (h_i) { + au_xino_write(sb, bindex, h_i->i_ino, /*ino*/0); + /* ignore this error */ + au_set_h_iptr(i, bindex, NULL, 0); + } + } + au_set_ibbot(i, a->btgt); +} + +/* ---------------------------------------------------------------------- */ + +/* mainly for link(2) and rename(2) */ +int au_wbr(struct dentry *dentry, aufs_bindex_t btgt) +{ + aufs_bindex_t bdiropq, bwh; + struct dentry *parent; + struct au_branch *br; + + parent = dentry->d_parent; + IMustLock(d_inode(parent)); /* dir is locked */ + + bdiropq = au_dbdiropq(parent); + bwh = au_dbwh(dentry); + br = au_sbr(dentry->d_sb, btgt); + if (au_br_rdonly(br) + || (0 <= bdiropq && bdiropq < btgt) + || (0 <= bwh && bwh < btgt)) + btgt = -1; + + AuDbg("btgt %d\n", btgt); + return btgt; +} + +/* sets src_btop, dst_btop and btgt */ +static int au_ren_wbr(struct au_ren_args *a) +{ + int err; + struct au_wr_dir_args wr_dir_args = { + /* .force_btgt = -1, */ + .flags = AuWrDir_ADD_ENTRY + }; + + a->src_btop = au_dbtop(a->src_dentry); + a->dst_btop = au_dbtop(a->dst_dentry); + if (au_ftest_ren(a->auren_flags, ISDIR_SRC) + || au_ftest_ren(a->auren_flags, ISDIR_DST)) + au_fset_wrdir(wr_dir_args.flags, ISDIR); + wr_dir_args.force_btgt = a->src_btop; + if (a->dst_inode && a->dst_btop < a->src_btop) + wr_dir_args.force_btgt = a->dst_btop; + wr_dir_args.force_btgt = au_wbr(a->dst_dentry, wr_dir_args.force_btgt); + err = au_wr_dir(a->dst_dentry, a->src_dentry, &wr_dir_args); + a->btgt = err; + if (a->exchange) + au_update_dbtop(a->dst_dentry); + + return err; +} + +static void au_ren_dt(struct au_ren_args *a) +{ + a->h_path.dentry = a->src_h_parent; + au_dtime_store(a->src_dt + AuPARENT, a->src_parent, &a->h_path); + if (!au_ftest_ren(a->auren_flags, ISSAMEDIR)) { + a->h_path.dentry = a->dst_h_parent; + au_dtime_store(a->dst_dt + AuPARENT, a->dst_parent, &a->h_path); + } + + au_fclr_ren(a->auren_flags, DT_DSTDIR); + if (!au_ftest_ren(a->auren_flags, ISDIR_SRC) + && !a->exchange) + return; + + a->h_path.dentry = a->src_h_dentry; + au_dtime_store(a->src_dt + AuCHILD, a->src_dentry, &a->h_path); + if (d_is_positive(a->dst_h_dentry)) { + au_fset_ren(a->auren_flags, DT_DSTDIR); + a->h_path.dentry = a->dst_h_dentry; + au_dtime_store(a->dst_dt + AuCHILD, a->dst_dentry, &a->h_path); + } +} + +static void au_ren_rev_dt(int err, struct au_ren_args *a) +{ + struct dentry *h_d; + struct inode *h_inode; + + au_dtime_revert(a->src_dt + AuPARENT); + if (!au_ftest_ren(a->auren_flags, ISSAMEDIR)) + au_dtime_revert(a->dst_dt + AuPARENT); + + if (au_ftest_ren(a->auren_flags, ISDIR_SRC) && err != -EIO) { + h_d = a->src_dt[AuCHILD].dt_h_path.dentry; + h_inode = d_inode(h_d); + inode_lock_nested(h_inode, AuLsc_I_CHILD); + au_dtime_revert(a->src_dt + AuCHILD); + inode_unlock(h_inode); + + if (au_ftest_ren(a->auren_flags, DT_DSTDIR)) { + h_d = a->dst_dt[AuCHILD].dt_h_path.dentry; + h_inode = d_inode(h_d); + inode_lock_nested(h_inode, AuLsc_I_CHILD); + au_dtime_revert(a->dst_dt + AuCHILD); + inode_unlock(h_inode); + } + } +} + +/* ---------------------------------------------------------------------- */ + +int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry, + struct inode *_dst_dir, struct dentry *_dst_dentry, + unsigned int _flags) +{ + int err, lock_flags; + void *rev; + /* reduce stack space */ + struct au_ren_args *a; + struct au_pin pin; + + AuDbg("%pd, %pd, 0x%x\n", _src_dentry, _dst_dentry, _flags); + IMustLock(_src_dir); + IMustLock(_dst_dir); + + err = -EINVAL; + if (unlikely(_flags & RENAME_WHITEOUT)) + goto out; + + err = -ENOMEM; + BUILD_BUG_ON(sizeof(*a) > PAGE_SIZE); + a = kzalloc(sizeof(*a), GFP_NOFS); + if (unlikely(!a)) + goto out; + + a->flags = _flags; + BUILD_BUG_ON(sizeof(a->exchange) == sizeof(u8) + && RENAME_EXCHANGE > U8_MAX); + a->exchange = _flags & RENAME_EXCHANGE; + a->src_dir = _src_dir; + a->src_dentry = _src_dentry; + a->src_inode = NULL; + if (d_really_is_positive(a->src_dentry)) + a->src_inode = d_inode(a->src_dentry); + a->src_parent = a->src_dentry->d_parent; /* dir inode is locked */ + a->dst_dir = _dst_dir; + a->dst_dentry = _dst_dentry; + a->dst_inode = NULL; + if (d_really_is_positive(a->dst_dentry)) + a->dst_inode = d_inode(a->dst_dentry); + a->dst_parent = a->dst_dentry->d_parent; /* dir inode is locked */ + if (a->dst_inode) { + /* + * if EXCHANGE && src is non-dir && dst is dir, + * dst is not locked. + */ + /* IMustLock(a->dst_inode); */ + au_igrab(a->dst_inode); + } + + err = -ENOTDIR; + lock_flags = AuLock_FLUSH | AuLock_NOPLM | AuLock_GEN; + if (d_is_dir(a->src_dentry)) { + au_fset_ren(a->auren_flags, ISDIR_SRC); + if (unlikely(!a->exchange + && d_really_is_positive(a->dst_dentry) + && !d_is_dir(a->dst_dentry))) + goto out_free; + lock_flags |= AuLock_DIRS; + } + if (a->dst_inode && d_is_dir(a->dst_dentry)) { + au_fset_ren(a->auren_flags, ISDIR_DST); + if (unlikely(!a->exchange + && d_really_is_positive(a->src_dentry) + && !d_is_dir(a->src_dentry))) + goto out_free; + lock_flags |= AuLock_DIRS; + } + err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, + lock_flags); + if (unlikely(err)) + goto out_free; + + err = au_d_hashed_positive(a->src_dentry); + if (unlikely(err)) + goto out_unlock; + err = -ENOENT; + if (a->dst_inode) { + /* + * If it is a dir, VFS unhash it before this + * function. It means we cannot rely upon d_unhashed(). + */ + if (unlikely(!a->dst_inode->i_nlink)) + goto out_unlock; + if (!au_ftest_ren(a->auren_flags, ISDIR_DST)) { + err = au_d_hashed_positive(a->dst_dentry); + if (unlikely(err && !a->exchange)) + goto out_unlock; + } else if (unlikely(IS_DEADDIR(a->dst_inode))) + goto out_unlock; + } else if (unlikely(d_unhashed(a->dst_dentry))) + goto out_unlock; + + /* + * is it possible? + * yes, it happened (in linux-3.3-rcN) but I don't know why. + * there may exist a problem somewhere else. + */ + err = -EINVAL; + if (unlikely(d_inode(a->dst_parent) == d_inode(a->src_dentry))) + goto out_unlock; + + au_fset_ren(a->auren_flags, ISSAMEDIR); /* temporary */ + di_write_lock_parent(a->dst_parent); + + /* which branch we process */ + err = au_ren_wbr(a); + if (unlikely(err < 0)) + goto out_parent; + a->br = au_sbr(a->dst_dentry->d_sb, a->btgt); + a->h_path.mnt = au_br_mnt(a->br); + + /* are they available to be renamed */ + err = au_ren_may_dir(a); + if (unlikely(err)) + goto out_children; + + /* prepare the writable parent dir on the same branch */ + if (a->dst_btop == a->btgt) { + au_fset_ren(a->auren_flags, WHDST); + } else { + err = au_cpup_dirs(a->dst_dentry, a->btgt); + if (unlikely(err)) + goto out_children; + } + + err = 0; + if (!a->exchange) { + if (a->src_dir != a->dst_dir) { + /* + * this temporary unlock is safe, + * because both dir->i_mutex are locked. + */ + di_write_unlock(a->dst_parent); + di_write_lock_parent(a->src_parent); + err = au_wr_dir_need_wh(a->src_dentry, + au_ftest_ren(a->auren_flags, + ISDIR_SRC), + &a->btgt); + di_write_unlock(a->src_parent); + di_write_lock2_parent(a->src_parent, a->dst_parent, + /*isdir*/1); + au_fclr_ren(a->auren_flags, ISSAMEDIR); + } else + err = au_wr_dir_need_wh(a->src_dentry, + au_ftest_ren(a->auren_flags, + ISDIR_SRC), + &a->btgt); + } + if (unlikely(err < 0)) + goto out_children; + if (err) + au_fset_ren(a->auren_flags, WHSRC); + + /* cpup src */ + if (a->src_btop != a->btgt) { + err = au_pin(&pin, a->src_dentry, a->btgt, + au_opt_udba(a->src_dentry->d_sb), + AuPin_DI_LOCKED | AuPin_MNT_WRITE); + if (!err) { + struct au_cp_generic cpg = { + .dentry = a->src_dentry, + .bdst = a->btgt, + .bsrc = a->src_btop, + .len = -1, + .pin = &pin, + .flags = AuCpup_DTIME | AuCpup_HOPEN + }; + AuDebugOn(au_dbtop(a->src_dentry) != a->src_btop); + err = au_sio_cpup_simple(&cpg); + au_unpin(&pin); + } + if (unlikely(err)) + goto out_children; + a->src_btop = a->btgt; + a->src_h_dentry = au_h_dptr(a->src_dentry, a->btgt); + if (!a->exchange) + au_fset_ren(a->auren_flags, WHSRC); + } + + /* cpup dst */ + if (a->exchange && a->dst_inode + && a->dst_btop != a->btgt) { + err = au_pin(&pin, a->dst_dentry, a->btgt, + au_opt_udba(a->dst_dentry->d_sb), + AuPin_DI_LOCKED | AuPin_MNT_WRITE); + if (!err) { + struct au_cp_generic cpg = { + .dentry = a->dst_dentry, + .bdst = a->btgt, + .bsrc = a->dst_btop, + .len = -1, + .pin = &pin, + .flags = AuCpup_DTIME | AuCpup_HOPEN + }; + err = au_sio_cpup_simple(&cpg); + au_unpin(&pin); + } + if (unlikely(err)) + goto out_children; + a->dst_btop = a->btgt; + a->dst_h_dentry = au_h_dptr(a->dst_dentry, a->btgt); + } + + /* lock them all */ + err = au_ren_lock(a); + if (unlikely(err)) + /* leave the copied-up one */ + goto out_children; + + if (!a->exchange) { + if (!au_opt_test(au_mntflags(a->dst_dir->i_sb), UDBA_NONE)) + err = au_may_ren(a); + else if (unlikely(a->dst_dentry->d_name.len > AUFS_MAX_NAMELEN)) + err = -ENAMETOOLONG; + if (unlikely(err)) + goto out_hdir; + } + + /* store timestamps to be revertible */ + au_ren_dt(a); + + /* store dirren info */ + if (au_ftest_ren(a->auren_flags, DIRREN)) { + err = au_dr_rename(a->src_dentry, a->btgt, + &a->dst_dentry->d_name, &rev); + AuTraceErr(err); + if (unlikely(err)) + goto out_dt; + } + + /* here we go */ + err = do_rename(a); + if (unlikely(err)) + goto out_dirren; + + if (au_ftest_ren(a->auren_flags, DIRREN)) + au_dr_rename_fin(a->src_dentry, a->btgt, rev); + + /* update dir attributes */ + au_ren_refresh_dir(a); + + /* dput/iput all lower dentries */ + au_ren_refresh(a); + + goto out_hdir; /* success */ + +out_dirren: + if (au_ftest_ren(a->auren_flags, DIRREN)) + au_dr_rename_rev(a->src_dentry, a->btgt, rev); +out_dt: + au_ren_rev_dt(err, a); +out_hdir: + au_ren_unlock(a); +out_children: + au_nhash_wh_free(&a->whlist); + if (err && a->dst_inode && a->dst_btop != a->btgt) { + AuDbg("btop %d, btgt %d\n", a->dst_btop, a->btgt); + au_set_h_dptr(a->dst_dentry, a->btgt, NULL); + au_set_dbtop(a->dst_dentry, a->dst_btop); + } +out_parent: + if (!err) { + if (d_unhashed(a->src_dentry)) + au_fset_ren(a->auren_flags, DROPPED_SRC); + if (d_unhashed(a->dst_dentry)) + au_fset_ren(a->auren_flags, DROPPED_DST); + if (!a->exchange) + d_move(a->src_dentry, a->dst_dentry); + else { + d_exchange(a->src_dentry, a->dst_dentry); + if (au_ftest_ren(a->auren_flags, DROPPED_DST)) + d_drop(a->dst_dentry); + } + if (au_ftest_ren(a->auren_flags, DROPPED_SRC)) + d_drop(a->src_dentry); + } else { + au_update_dbtop(a->dst_dentry); + if (!a->dst_inode) + d_drop(a->dst_dentry); + } + if (au_ftest_ren(a->auren_flags, ISSAMEDIR)) + di_write_unlock(a->dst_parent); + else + di_write_unlock2(a->src_parent, a->dst_parent); +out_unlock: + aufs_read_and_write_unlock2(a->dst_dentry, a->src_dentry); +out_free: + iput(a->dst_inode); + if (a->thargs) + au_whtmp_rmdir_free(a->thargs); + au_kfree_rcu(a); +out: + AuTraceErr(err); + return err; +} diff --git a/fs/aufs/iinfo.c b/fs/aufs/iinfo.c new file mode 100644 index 0000000000000..f3ee066219ae0 --- /dev/null +++ b/fs/aufs/iinfo.c @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * inode private data + */ + +#include "aufs.h" + +struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex) +{ + struct inode *h_inode; + struct au_hinode *hinode; + + IiMustAnyLock(inode); + + hinode = au_hinode(au_ii(inode), bindex); + h_inode = hinode->hi_inode; + AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0); + return h_inode; +} + +/* todo: hard/soft set? */ +void au_hiput(struct au_hinode *hinode) +{ + au_hn_free(hinode); + dput(hinode->hi_whdentry); + iput(hinode->hi_inode); +} + +unsigned int au_hi_flags(struct inode *inode, int isdir) +{ + unsigned int flags; + const unsigned int mnt_flags = au_mntflags(inode->i_sb); + + flags = 0; + if (au_opt_test(mnt_flags, XINO)) + au_fset_hi(flags, XINO); + if (isdir && au_opt_test(mnt_flags, UDBA_HNOTIFY)) + au_fset_hi(flags, HNOTIFY); + return flags; +} + +void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex, + struct inode *h_inode, unsigned int flags) +{ + struct au_hinode *hinode; + struct inode *hi; + struct au_iinfo *iinfo = au_ii(inode); + + IiMustWriteLock(inode); + + hinode = au_hinode(iinfo, bindex); + hi = hinode->hi_inode; + AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0); + + if (hi) + au_hiput(hinode); + hinode->hi_inode = h_inode; + if (h_inode) { + int err; + struct super_block *sb = inode->i_sb; + struct au_branch *br; + + AuDebugOn(inode->i_mode + && (h_inode->i_mode & S_IFMT) + != (inode->i_mode & S_IFMT)); + if (bindex == iinfo->ii_btop) + au_cpup_igen(inode, h_inode); + br = au_sbr(sb, bindex); + hinode->hi_id = br->br_id; + if (au_ftest_hi(flags, XINO)) { + err = au_xino_write(sb, bindex, h_inode->i_ino, + inode->i_ino); + if (unlikely(err)) + AuIOErr1("failed au_xino_write() %d\n", err); + } + + if (au_ftest_hi(flags, HNOTIFY) + && au_br_hnotifyable(br->br_perm)) { + err = au_hn_alloc(hinode, inode); + if (unlikely(err)) + AuIOErr1("au_hn_alloc() %d\n", err); + } + } +} + +void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex, + struct dentry *h_wh) +{ + struct au_hinode *hinode; + + IiMustWriteLock(inode); + + hinode = au_hinode(au_ii(inode), bindex); + AuDebugOn(hinode->hi_whdentry); + hinode->hi_whdentry = h_wh; +} + +void au_update_iigen(struct inode *inode, int half) +{ + struct au_iinfo *iinfo; + struct au_iigen *iigen; + unsigned int sigen; + + sigen = au_sigen(inode->i_sb); + iinfo = au_ii(inode); + iigen = &iinfo->ii_generation; + spin_lock(&iigen->ig_spin); + iigen->ig_generation = sigen; + if (half) + au_ig_fset(iigen->ig_flags, HALF_REFRESHED); + else + au_ig_fclr(iigen->ig_flags, HALF_REFRESHED); + spin_unlock(&iigen->ig_spin); +} + +/* it may be called at remount time, too */ +void au_update_ibrange(struct inode *inode, int do_put_zero) +{ + struct au_iinfo *iinfo; + aufs_bindex_t bindex, bbot; + + AuDebugOn(au_is_bad_inode(inode)); + IiMustWriteLock(inode); + + iinfo = au_ii(inode); + if (do_put_zero && iinfo->ii_btop >= 0) { + for (bindex = iinfo->ii_btop; bindex <= iinfo->ii_bbot; + bindex++) { + struct inode *h_i; + + h_i = au_hinode(iinfo, bindex)->hi_inode; + if (h_i + && !h_i->i_nlink + && !(h_i->i_state & I_LINKABLE)) + au_set_h_iptr(inode, bindex, NULL, 0); + } + } + + iinfo->ii_btop = -1; + iinfo->ii_bbot = -1; + bbot = au_sbbot(inode->i_sb); + for (bindex = 0; bindex <= bbot; bindex++) + if (au_hinode(iinfo, bindex)->hi_inode) { + iinfo->ii_btop = bindex; + break; + } + if (iinfo->ii_btop >= 0) + for (bindex = bbot; bindex >= iinfo->ii_btop; bindex--) + if (au_hinode(iinfo, bindex)->hi_inode) { + iinfo->ii_bbot = bindex; + break; + } + AuDebugOn(iinfo->ii_btop > iinfo->ii_bbot); +} + +/* ---------------------------------------------------------------------- */ + +void au_icntnr_init_once(void *_c) +{ + struct au_icntnr *c = _c; + struct au_iinfo *iinfo = &c->iinfo; + + spin_lock_init(&iinfo->ii_generation.ig_spin); + au_rw_init(&iinfo->ii_rwsem); + inode_init_once(&c->vfs_inode); +} + +void au_hinode_init(struct au_hinode *hinode) +{ + hinode->hi_inode = NULL; + hinode->hi_id = -1; + au_hn_init(hinode); + hinode->hi_whdentry = NULL; +} + +int au_iinfo_init(struct inode *inode) +{ + struct au_iinfo *iinfo; + struct super_block *sb; + struct au_hinode *hi; + int nbr, i; + + sb = inode->i_sb; + iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo); + nbr = au_sbbot(sb) + 1; + if (unlikely(nbr <= 0)) + nbr = 1; + hi = kmalloc_array(nbr, sizeof(*iinfo->ii_hinode), GFP_NOFS); + if (hi) { + au_lcnt_inc(&au_sbi(sb)->si_ninodes); + + iinfo->ii_hinode = hi; + for (i = 0; i < nbr; i++, hi++) + au_hinode_init(hi); + + iinfo->ii_generation.ig_generation = au_sigen(sb); + iinfo->ii_btop = -1; + iinfo->ii_bbot = -1; + iinfo->ii_vdir = NULL; + return 0; + } + return -ENOMEM; +} + +int au_hinode_realloc(struct au_iinfo *iinfo, int nbr, int may_shrink) +{ + int err, i; + struct au_hinode *hip; + + AuRwMustWriteLock(&iinfo->ii_rwsem); + + err = -ENOMEM; + hip = au_krealloc(iinfo->ii_hinode, sizeof(*hip) * nbr, GFP_NOFS, + may_shrink); + if (hip) { + iinfo->ii_hinode = hip; + i = iinfo->ii_bbot + 1; + hip += i; + for (; i < nbr; i++, hip++) + au_hinode_init(hip); + err = 0; + } + + return err; +} + +void au_iinfo_fin(struct inode *inode) +{ + struct au_iinfo *iinfo; + struct au_hinode *hi; + struct super_block *sb; + aufs_bindex_t bindex, bbot; + const unsigned char unlinked = !inode->i_nlink; + + AuDebugOn(au_is_bad_inode(inode)); + + sb = inode->i_sb; + au_lcnt_dec(&au_sbi(sb)->si_ninodes); + if (si_pid_test(sb)) + au_xino_delete_inode(inode, unlinked); + else { + /* + * it is safe to hide the dependency between sbinfo and + * sb->s_umount. + */ + lockdep_off(); + si_noflush_read_lock(sb); + au_xino_delete_inode(inode, unlinked); + si_read_unlock(sb); + lockdep_on(); + } + + iinfo = au_ii(inode); + if (iinfo->ii_vdir) + au_vdir_free(iinfo->ii_vdir); + + bindex = iinfo->ii_btop; + if (bindex >= 0) { + hi = au_hinode(iinfo, bindex); + bbot = iinfo->ii_bbot; + while (bindex++ <= bbot) { + if (hi->hi_inode) + au_hiput(hi); + hi++; + } + } + au_kfree_rcu(iinfo->ii_hinode); + AuRwDestroy(&iinfo->ii_rwsem); +} diff --git a/fs/aufs/inode.c b/fs/aufs/inode.c new file mode 100644 index 0000000000000..978cc1cc46c2d --- /dev/null +++ b/fs/aufs/inode.c @@ -0,0 +1,529 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * inode functions + */ + +#include +#include "aufs.h" + +struct inode *au_igrab(struct inode *inode) +{ + if (inode) { + AuDebugOn(!atomic_read(&inode->i_count)); + ihold(inode); + } + return inode; +} + +static void au_refresh_hinode_attr(struct inode *inode, int do_version) +{ + au_cpup_attr_all(inode, /*force*/0); + au_update_iigen(inode, /*half*/1); + if (do_version) + inode_inc_iversion(inode); +} + +static int au_ii_refresh(struct inode *inode, int *update) +{ + int err, e, nbr; + umode_t type; + aufs_bindex_t bindex, new_bindex; + struct super_block *sb; + struct au_iinfo *iinfo; + struct au_hinode *p, *q, tmp; + + AuDebugOn(au_is_bad_inode(inode)); + IiMustWriteLock(inode); + + *update = 0; + sb = inode->i_sb; + nbr = au_sbbot(sb) + 1; + type = inode->i_mode & S_IFMT; + iinfo = au_ii(inode); + err = au_hinode_realloc(iinfo, nbr, /*may_shrink*/0); + if (unlikely(err)) + goto out; + + AuDebugOn(iinfo->ii_btop < 0); + p = au_hinode(iinfo, iinfo->ii_btop); + for (bindex = iinfo->ii_btop; bindex <= iinfo->ii_bbot; + bindex++, p++) { + if (!p->hi_inode) + continue; + + AuDebugOn(type != (p->hi_inode->i_mode & S_IFMT)); + new_bindex = au_br_index(sb, p->hi_id); + if (new_bindex == bindex) + continue; + + if (new_bindex < 0) { + *update = 1; + au_hiput(p); + p->hi_inode = NULL; + continue; + } + + if (new_bindex < iinfo->ii_btop) + iinfo->ii_btop = new_bindex; + if (iinfo->ii_bbot < new_bindex) + iinfo->ii_bbot = new_bindex; + /* swap two lower inode, and loop again */ + q = au_hinode(iinfo, new_bindex); + tmp = *q; + *q = *p; + *p = tmp; + if (tmp.hi_inode) { + bindex--; + p--; + } + } + au_update_ibrange(inode, /*do_put_zero*/0); + au_hinode_realloc(iinfo, nbr, /*may_shrink*/1); /* harmless if err */ + e = au_dy_irefresh(inode); + if (unlikely(e && !err)) + err = e; + +out: + AuTraceErr(err); + return err; +} + +void au_refresh_iop(struct inode *inode, int force_getattr) +{ + int type; + struct au_sbinfo *sbi = au_sbi(inode->i_sb); + const struct inode_operations *iop + = force_getattr ? aufs_iop : sbi->si_iop_array; + + if (inode->i_op == iop) + return; + + switch (inode->i_mode & S_IFMT) { + case S_IFDIR: + type = AuIop_DIR; + break; + case S_IFLNK: + type = AuIop_SYMLINK; + break; + default: + type = AuIop_OTHER; + break; + } + + inode->i_op = iop + type; + /* unnecessary smp_wmb() */ +} + +int au_refresh_hinode_self(struct inode *inode) +{ + int err, update; + + err = au_ii_refresh(inode, &update); + if (!err) + au_refresh_hinode_attr(inode, update && S_ISDIR(inode->i_mode)); + + AuTraceErr(err); + return err; +} + +int au_refresh_hinode(struct inode *inode, struct dentry *dentry) +{ + int err, e, update; + unsigned int flags; + umode_t mode; + aufs_bindex_t bindex, bbot; + unsigned char isdir; + struct au_hinode *p; + struct au_iinfo *iinfo; + + err = au_ii_refresh(inode, &update); + if (unlikely(err)) + goto out; + + update = 0; + iinfo = au_ii(inode); + p = au_hinode(iinfo, iinfo->ii_btop); + mode = (inode->i_mode & S_IFMT); + isdir = S_ISDIR(mode); + flags = au_hi_flags(inode, isdir); + bbot = au_dbbot(dentry); + for (bindex = au_dbtop(dentry); bindex <= bbot; bindex++) { + struct inode *h_i, *h_inode; + struct dentry *h_d; + + h_d = au_h_dptr(dentry, bindex); + if (!h_d || d_is_negative(h_d)) + continue; + + h_inode = d_inode(h_d); + AuDebugOn(mode != (h_inode->i_mode & S_IFMT)); + if (iinfo->ii_btop <= bindex && bindex <= iinfo->ii_bbot) { + h_i = au_h_iptr(inode, bindex); + if (h_i) { + if (h_i == h_inode) + continue; + err = -EIO; + break; + } + } + if (bindex < iinfo->ii_btop) + iinfo->ii_btop = bindex; + if (iinfo->ii_bbot < bindex) + iinfo->ii_bbot = bindex; + au_set_h_iptr(inode, bindex, au_igrab(h_inode), flags); + update = 1; + } + au_update_ibrange(inode, /*do_put_zero*/0); + e = au_dy_irefresh(inode); + if (unlikely(e && !err)) + err = e; + if (!err) + au_refresh_hinode_attr(inode, update && isdir); + +out: + AuTraceErr(err); + return err; +} + +static int set_inode(struct inode *inode, struct dentry *dentry) +{ + int err; + unsigned int flags; + umode_t mode; + aufs_bindex_t bindex, btop, btail; + unsigned char isdir; + struct dentry *h_dentry; + struct inode *h_inode; + struct au_iinfo *iinfo; + const struct inode_operations *iop; + + IiMustWriteLock(inode); + + err = 0; + isdir = 0; + iop = au_sbi(inode->i_sb)->si_iop_array; + btop = au_dbtop(dentry); + h_dentry = au_h_dptr(dentry, btop); + h_inode = d_inode(h_dentry); + mode = h_inode->i_mode; + switch (mode & S_IFMT) { + case S_IFREG: + btail = au_dbtail(dentry); + inode->i_op = iop + AuIop_OTHER; + inode->i_fop = &aufs_file_fop; + err = au_dy_iaop(inode, btop, h_inode); + if (unlikely(err)) + goto out; + break; + case S_IFDIR: + isdir = 1; + btail = au_dbtaildir(dentry); + inode->i_op = iop + AuIop_DIR; + inode->i_fop = &aufs_dir_fop; + break; + case S_IFLNK: + btail = au_dbtail(dentry); + inode->i_op = iop + AuIop_SYMLINK; + break; + case S_IFBLK: + case S_IFCHR: + case S_IFIFO: + case S_IFSOCK: + btail = au_dbtail(dentry); + inode->i_op = iop + AuIop_OTHER; + init_special_inode(inode, mode, h_inode->i_rdev); + break; + default: + AuIOErr("Unknown file type 0%o\n", mode); + err = -EIO; + goto out; + } + + /* do not set hnotify for whiteouted dirs (SHWH mode) */ + flags = au_hi_flags(inode, isdir); + if (au_opt_test(au_mntflags(dentry->d_sb), SHWH) + && au_ftest_hi(flags, HNOTIFY) + && dentry->d_name.len > AUFS_WH_PFX_LEN + && !memcmp(dentry->d_name.name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) + au_fclr_hi(flags, HNOTIFY); + iinfo = au_ii(inode); + iinfo->ii_btop = btop; + iinfo->ii_bbot = btail; + for (bindex = btop; bindex <= btail; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (h_dentry) + au_set_h_iptr(inode, bindex, + au_igrab(d_inode(h_dentry)), flags); + } + au_cpup_attr_all(inode, /*force*/1); + /* + * to force calling aufs_get_acl() every time, + * do not call cache_no_acl() for aufs inode. + */ + +out: + return err; +} + +/* + * successful returns with iinfo write_locked + * minus: errno + * zero: success, matched + * plus: no error, but unmatched + */ +static int reval_inode(struct inode *inode, struct dentry *dentry) +{ + int err; + unsigned int gen, igflags; + aufs_bindex_t bindex, bbot; + struct inode *h_inode, *h_dinode; + struct dentry *h_dentry; + + /* + * before this function, if aufs got any iinfo lock, it must be only + * one, the parent dir. + * it can happen by UDBA and the obsoleted inode number. + */ + err = -EIO; + if (unlikely(inode->i_ino == parent_ino(dentry))) + goto out; + + err = 1; + ii_write_lock_new_child(inode); + h_dentry = au_h_dptr(dentry, au_dbtop(dentry)); + h_dinode = d_inode(h_dentry); + bbot = au_ibbot(inode); + for (bindex = au_ibtop(inode); bindex <= bbot; bindex++) { + h_inode = au_h_iptr(inode, bindex); + if (!h_inode || h_inode != h_dinode) + continue; + + err = 0; + gen = au_iigen(inode, &igflags); + if (gen == au_digen(dentry) + && !au_ig_ftest(igflags, HALF_REFRESHED)) + break; + + /* fully refresh inode using dentry */ + err = au_refresh_hinode(inode, dentry); + if (!err) + au_update_iigen(inode, /*half*/0); + break; + } + + if (unlikely(err)) + ii_write_unlock(inode); +out: + return err; +} + +int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, + unsigned int d_type, ino_t *ino) +{ + int err, idx; + const int isnondir = d_type != DT_DIR; + + /* prevent hardlinked inode number from race condition */ + if (isnondir) { + err = au_xinondir_enter(sb, bindex, h_ino, &idx); + if (unlikely(err)) + goto out; + } + + err = au_xino_read(sb, bindex, h_ino, ino); + if (unlikely(err)) + goto out_xinondir; + + if (!*ino) { + err = -EIO; + *ino = au_xino_new_ino(sb); + if (unlikely(!*ino)) + goto out_xinondir; + err = au_xino_write(sb, bindex, h_ino, *ino); + if (unlikely(err)) + goto out_xinondir; + } + +out_xinondir: + if (isnondir && idx >= 0) + au_xinondir_leave(sb, bindex, h_ino, idx); +out: + return err; +} + +/* successful returns with iinfo write_locked */ +/* todo: return with unlocked? */ +struct inode *au_new_inode(struct dentry *dentry, int must_new) +{ + struct inode *inode, *h_inode; + struct dentry *h_dentry; + struct super_block *sb; + ino_t h_ino, ino; + int err, idx, hlinked; + aufs_bindex_t btop; + + sb = dentry->d_sb; + btop = au_dbtop(dentry); + h_dentry = au_h_dptr(dentry, btop); + h_inode = d_inode(h_dentry); + h_ino = h_inode->i_ino; + hlinked = !d_is_dir(h_dentry) && h_inode->i_nlink > 1; + +new_ino: + /* + * stop 'race'-ing between hardlinks under different + * parents. + */ + if (hlinked) { + err = au_xinondir_enter(sb, btop, h_ino, &idx); + inode = ERR_PTR(err); + if (unlikely(err)) + goto out; + } + + err = au_xino_read(sb, btop, h_ino, &ino); + inode = ERR_PTR(err); + if (unlikely(err)) + goto out_xinondir; + + if (!ino) { + ino = au_xino_new_ino(sb); + if (unlikely(!ino)) { + inode = ERR_PTR(-EIO); + goto out_xinondir; + } + } + + AuDbg("i%lu\n", (unsigned long)ino); + inode = au_iget_locked(sb, ino); + err = PTR_ERR(inode); + if (IS_ERR(inode)) + goto out_xinondir; + + AuDbg("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW)); + if (inode->i_state & I_NEW) { + ii_write_lock_new_child(inode); + err = set_inode(inode, dentry); + if (!err) { + unlock_new_inode(inode); + goto out_xinondir; /* success */ + } + + /* + * iget_failed() calls iput(), but we need to call + * ii_write_unlock() after iget_failed(). so dirty hack for + * i_count. + */ + atomic_inc(&inode->i_count); + iget_failed(inode); + ii_write_unlock(inode); + au_xino_write(sb, btop, h_ino, /*ino*/0); + /* ignore this error */ + goto out_iput; + } else if (!must_new && !IS_DEADDIR(inode) && inode->i_nlink) { + /* + * horrible race condition between lookup, readdir and copyup + * (or something). + */ + if (hlinked && idx >= 0) + au_xinondir_leave(sb, btop, h_ino, idx); + err = reval_inode(inode, dentry); + if (unlikely(err < 0)) { + hlinked = 0; + goto out_iput; + } + if (!err) + goto out; /* success */ + else if (hlinked && idx >= 0) { + err = au_xinondir_enter(sb, btop, h_ino, &idx); + if (unlikely(err)) { + iput(inode); + inode = ERR_PTR(err); + goto out; + } + } + } + + if (unlikely(au_test_fs_unique_ino(h_inode))) + AuWarn1("Warning: Un-notified UDBA or repeatedly renamed dir," + " b%d, %s, %pd, hi%lu, i%lu.\n", + btop, au_sbtype(h_dentry->d_sb), dentry, + (unsigned long)h_ino, (unsigned long)ino); + ino = 0; + err = au_xino_write(sb, btop, h_ino, /*ino*/0); + if (!err) { + iput(inode); + if (hlinked && idx >= 0) + au_xinondir_leave(sb, btop, h_ino, idx); + goto new_ino; + } + +out_iput: + iput(inode); + inode = ERR_PTR(err); +out_xinondir: + if (hlinked && idx >= 0) + au_xinondir_leave(sb, btop, h_ino, idx); +out: + return inode; +} + +/* ---------------------------------------------------------------------- */ + +int au_test_ro(struct super_block *sb, aufs_bindex_t bindex, + struct inode *inode) +{ + int err; + struct inode *hi; + + err = au_br_rdonly(au_sbr(sb, bindex)); + + /* pseudo-link after flushed may happen out of bounds */ + if (!err + && inode + && au_ibtop(inode) <= bindex + && bindex <= au_ibbot(inode)) { + /* + * permission check is unnecessary since vfsub routine + * will be called later + */ + hi = au_h_iptr(inode, bindex); + if (hi) + err = IS_IMMUTABLE(hi) ? -EROFS : 0; + } + + return err; +} + +int au_test_h_perm(struct inode *h_inode, int mask) +{ + if (uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) + return 0; + return inode_permission(h_inode, mask); +} + +int au_test_h_perm_sio(struct inode *h_inode, int mask) +{ + if (au_test_nfs(h_inode->i_sb) + && (mask & MAY_WRITE) + && S_ISDIR(h_inode->i_mode)) + mask |= MAY_READ; /* force permission check */ + return au_test_h_perm(h_inode, mask); +} diff --git a/fs/aufs/inode.h b/fs/aufs/inode.h new file mode 100644 index 0000000000000..967646a558bc3 --- /dev/null +++ b/fs/aufs/inode.h @@ -0,0 +1,698 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * inode operations + */ + +#ifndef __AUFS_INODE_H__ +#define __AUFS_INODE_H__ + +#ifdef __KERNEL__ + +#include +#include "rwsem.h" + +struct vfsmount; + +struct au_hnotify { +#ifdef CONFIG_AUFS_HNOTIFY +#ifdef CONFIG_AUFS_HFSNOTIFY + /* never use fsnotify_add_vfsmount_mark() */ + struct fsnotify_mark hn_mark; +#endif + struct inode *hn_aufs_inode; /* no get/put */ + struct rcu_head rcu; +#endif +} ____cacheline_aligned_in_smp; + +struct au_hinode { + struct inode *hi_inode; + aufs_bindex_t hi_id; +#ifdef CONFIG_AUFS_HNOTIFY + struct au_hnotify *hi_notify; +#endif + + /* reference to the copied-up whiteout with get/put */ + struct dentry *hi_whdentry; +}; + +/* ig_flags */ +#define AuIG_HALF_REFRESHED 1 +#define au_ig_ftest(flags, name) ((flags) & AuIG_##name) +#define au_ig_fset(flags, name) \ + do { (flags) |= AuIG_##name; } while (0) +#define au_ig_fclr(flags, name) \ + do { (flags) &= ~AuIG_##name; } while (0) + +struct au_iigen { + spinlock_t ig_spin; + __u32 ig_generation, ig_flags; +}; + +struct au_vdir; +struct au_iinfo { + struct au_iigen ii_generation; + struct super_block *ii_hsb1; /* no get/put */ + + struct au_rwsem ii_rwsem; + aufs_bindex_t ii_btop, ii_bbot; + __u32 ii_higen; + struct au_hinode *ii_hinode; + struct au_vdir *ii_vdir; +}; + +struct au_icntnr { + struct au_iinfo iinfo; + struct inode vfs_inode; + struct hlist_bl_node plink; + struct rcu_head rcu; +} ____cacheline_aligned_in_smp; + +/* au_pin flags */ +#define AuPin_DI_LOCKED 1 +#define AuPin_MNT_WRITE (1 << 1) +#define au_ftest_pin(flags, name) ((flags) & AuPin_##name) +#define au_fset_pin(flags, name) \ + do { (flags) |= AuPin_##name; } while (0) +#define au_fclr_pin(flags, name) \ + do { (flags) &= ~AuPin_##name; } while (0) + +struct au_pin { + /* input */ + struct dentry *dentry; + unsigned int udba; + unsigned char lsc_di, lsc_hi, flags; + aufs_bindex_t bindex; + + /* output */ + struct dentry *parent; + struct au_hinode *hdir; + struct vfsmount *h_mnt; + + /* temporary unlock/relock for copyup */ + struct dentry *h_dentry, *h_parent; + struct au_branch *br; + struct task_struct *task; +}; + +void au_pin_hdir_unlock(struct au_pin *p); +int au_pin_hdir_lock(struct au_pin *p); +int au_pin_hdir_relock(struct au_pin *p); +void au_pin_hdir_acquire_nest(struct au_pin *p); +void au_pin_hdir_release(struct au_pin *p); + +/* ---------------------------------------------------------------------- */ + +static inline struct au_iinfo *au_ii(struct inode *inode) +{ + BUG_ON(is_bad_inode(inode)); + return &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo); +} + +/* ---------------------------------------------------------------------- */ + +/* inode.c */ +struct inode *au_igrab(struct inode *inode); +void au_refresh_iop(struct inode *inode, int force_getattr); +int au_refresh_hinode_self(struct inode *inode); +int au_refresh_hinode(struct inode *inode, struct dentry *dentry); +int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, + unsigned int d_type, ino_t *ino); +struct inode *au_new_inode(struct dentry *dentry, int must_new); +int au_test_ro(struct super_block *sb, aufs_bindex_t bindex, + struct inode *inode); +int au_test_h_perm(struct inode *h_inode, int mask); +int au_test_h_perm_sio(struct inode *h_inode, int mask); + +static inline int au_wh_ino(struct super_block *sb, aufs_bindex_t bindex, + ino_t h_ino, unsigned int d_type, ino_t *ino) +{ +#ifdef CONFIG_AUFS_SHWH + return au_ino(sb, bindex, h_ino, d_type, ino); +#else + return 0; +#endif +} + +/* i_op.c */ +enum { + AuIop_SYMLINK, + AuIop_DIR, + AuIop_OTHER, + AuIop_Last +}; +extern struct inode_operations aufs_iop[AuIop_Last], /* not const */ + aufs_iop_nogetattr[AuIop_Last]; + +/* au_wr_dir flags */ +#define AuWrDir_ADD_ENTRY 1 +#define AuWrDir_ISDIR (1 << 1) +#define AuWrDir_TMPFILE (1 << 2) +#define au_ftest_wrdir(flags, name) ((flags) & AuWrDir_##name) +#define au_fset_wrdir(flags, name) \ + do { (flags) |= AuWrDir_##name; } while (0) +#define au_fclr_wrdir(flags, name) \ + do { (flags) &= ~AuWrDir_##name; } while (0) + +struct au_wr_dir_args { + aufs_bindex_t force_btgt; + unsigned char flags; +}; +int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, + struct au_wr_dir_args *args); + +struct dentry *au_pinned_h_parent(struct au_pin *pin); +void au_pin_init(struct au_pin *pin, struct dentry *dentry, + aufs_bindex_t bindex, int lsc_di, int lsc_hi, + unsigned int udba, unsigned char flags); +int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex, + unsigned int udba, unsigned char flags) __must_check; +int au_do_pin(struct au_pin *pin) __must_check; +void au_unpin(struct au_pin *pin); +int au_reval_for_attr(struct dentry *dentry, unsigned int sigen); + +#define AuIcpup_DID_CPUP 1 +#define au_ftest_icpup(flags, name) ((flags) & AuIcpup_##name) +#define au_fset_icpup(flags, name) \ + do { (flags) |= AuIcpup_##name; } while (0) +#define au_fclr_icpup(flags, name) \ + do { (flags) &= ~AuIcpup_##name; } while (0) + +struct au_icpup_args { + unsigned char flags; + unsigned char pin_flags; + aufs_bindex_t btgt; + unsigned int udba; + struct au_pin pin; + struct path h_path; + struct inode *h_inode; +}; + +int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia, + struct au_icpup_args *a); + +int au_h_path_getattr(struct dentry *dentry, struct inode *inode, int force, + struct path *h_path, int locked); + +/* i_op_add.c */ +int au_may_add(struct dentry *dentry, aufs_bindex_t bindex, + struct dentry *h_parent, int isdir); +int aufs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, + dev_t dev); +int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname); +int aufs_create(struct inode *dir, struct dentry *dentry, umode_t mode, + bool want_excl); +struct vfsub_aopen_args; +int au_aopen_or_create(struct inode *dir, struct dentry *dentry, + struct vfsub_aopen_args *args); +int aufs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode); +int aufs_link(struct dentry *src_dentry, struct inode *dir, + struct dentry *dentry); +int aufs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode); + +/* i_op_del.c */ +int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup); +int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, + struct dentry *h_parent, int isdir); +int aufs_unlink(struct inode *dir, struct dentry *dentry); +int aufs_rmdir(struct inode *dir, struct dentry *dentry); + +/* i_op_ren.c */ +int au_wbr(struct dentry *dentry, aufs_bindex_t btgt); +int aufs_rename(struct inode *src_dir, struct dentry *src_dentry, + struct inode *dir, struct dentry *dentry, + unsigned int flags); + +/* iinfo.c */ +struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex); +void au_hiput(struct au_hinode *hinode); +void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex, + struct dentry *h_wh); +unsigned int au_hi_flags(struct inode *inode, int isdir); + +/* hinode flags */ +#define AuHi_XINO 1 +#define AuHi_HNOTIFY (1 << 1) +#define au_ftest_hi(flags, name) ((flags) & AuHi_##name) +#define au_fset_hi(flags, name) \ + do { (flags) |= AuHi_##name; } while (0) +#define au_fclr_hi(flags, name) \ + do { (flags) &= ~AuHi_##name; } while (0) + +#ifndef CONFIG_AUFS_HNOTIFY +#undef AuHi_HNOTIFY +#define AuHi_HNOTIFY 0 +#endif + +void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex, + struct inode *h_inode, unsigned int flags); + +void au_update_iigen(struct inode *inode, int half); +void au_update_ibrange(struct inode *inode, int do_put_zero); + +void au_icntnr_init_once(void *_c); +void au_hinode_init(struct au_hinode *hinode); +int au_iinfo_init(struct inode *inode); +void au_iinfo_fin(struct inode *inode); +int au_hinode_realloc(struct au_iinfo *iinfo, int nbr, int may_shrink); + +#ifdef CONFIG_PROC_FS +/* plink.c */ +int au_plink_maint(struct super_block *sb, int flags); +struct au_sbinfo; +void au_plink_maint_leave(struct au_sbinfo *sbinfo); +int au_plink_maint_enter(struct super_block *sb); +#ifdef CONFIG_AUFS_DEBUG +void au_plink_list(struct super_block *sb); +#else +AuStubVoid(au_plink_list, struct super_block *sb) +#endif +int au_plink_test(struct inode *inode); +struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex); +void au_plink_append(struct inode *inode, aufs_bindex_t bindex, + struct dentry *h_dentry); +void au_plink_put(struct super_block *sb, int verbose); +void au_plink_clean(struct super_block *sb, int verbose); +void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id); +#else +AuStubInt0(au_plink_maint, struct super_block *sb, int flags); +AuStubVoid(au_plink_maint_leave, struct au_sbinfo *sbinfo); +AuStubInt0(au_plink_maint_enter, struct super_block *sb); +AuStubVoid(au_plink_list, struct super_block *sb); +AuStubInt0(au_plink_test, struct inode *inode); +AuStub(struct dentry *, au_plink_lkup, return NULL, + struct inode *inode, aufs_bindex_t bindex); +AuStubVoid(au_plink_append, struct inode *inode, aufs_bindex_t bindex, + struct dentry *h_dentry); +AuStubVoid(au_plink_put, struct super_block *sb, int verbose); +AuStubVoid(au_plink_clean, struct super_block *sb, int verbose); +AuStubVoid(au_plink_half_refresh, struct super_block *sb, aufs_bindex_t br_id); +#endif /* CONFIG_PROC_FS */ + +#ifdef CONFIG_AUFS_XATTR +/* xattr.c */ +int au_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, int ignore_flags, + unsigned int verbose); +ssize_t aufs_listxattr(struct dentry *dentry, char *list, size_t size); +void au_xattr_init(struct super_block *sb); +#else +AuStubInt0(au_cpup_xattr, struct dentry *h_dst, struct dentry *h_src, + int ignore_flags, unsigned int verbose); +AuStubVoid(au_xattr_init, struct super_block *sb); +#endif + +#ifdef CONFIG_FS_POSIX_ACL +struct posix_acl *aufs_get_acl(struct inode *inode, int type); +int aufs_set_acl(struct inode *inode, struct posix_acl *acl, int type); +#endif + +#if IS_ENABLED(CONFIG_AUFS_XATTR) || IS_ENABLED(CONFIG_FS_POSIX_ACL) +enum { + AU_XATTR_SET, + AU_ACL_SET +}; + +struct au_sxattr { + int type; + union { + struct { + const char *name; + const void *value; + size_t size; + int flags; + } set; + struct { + struct posix_acl *acl; + int type; + } acl_set; + } u; +}; +ssize_t au_sxattr(struct dentry *dentry, struct inode *inode, + struct au_sxattr *arg); +#endif + +/* ---------------------------------------------------------------------- */ + +/* lock subclass for iinfo */ +enum { + AuLsc_II_CHILD, /* child first */ + AuLsc_II_CHILD2, /* rename(2), link(2), and cpup at hnotify */ + AuLsc_II_CHILD3, /* copyup dirs */ + AuLsc_II_PARENT, /* see AuLsc_I_PARENT in vfsub.h */ + AuLsc_II_PARENT2, + AuLsc_II_PARENT3, /* copyup dirs */ + AuLsc_II_NEW_CHILD +}; + +/* + * ii_read_lock_child, ii_write_lock_child, + * ii_read_lock_child2, ii_write_lock_child2, + * ii_read_lock_child3, ii_write_lock_child3, + * ii_read_lock_parent, ii_write_lock_parent, + * ii_read_lock_parent2, ii_write_lock_parent2, + * ii_read_lock_parent3, ii_write_lock_parent3, + * ii_read_lock_new_child, ii_write_lock_new_child, + */ +#define AuReadLockFunc(name, lsc) \ +static inline void ii_read_lock_##name(struct inode *i) \ +{ \ + au_rw_read_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \ +} + +#define AuWriteLockFunc(name, lsc) \ +static inline void ii_write_lock_##name(struct inode *i) \ +{ \ + au_rw_write_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \ +} + +#define AuRWLockFuncs(name, lsc) \ + AuReadLockFunc(name, lsc) \ + AuWriteLockFunc(name, lsc) + +AuRWLockFuncs(child, CHILD); +AuRWLockFuncs(child2, CHILD2); +AuRWLockFuncs(child3, CHILD3); +AuRWLockFuncs(parent, PARENT); +AuRWLockFuncs(parent2, PARENT2); +AuRWLockFuncs(parent3, PARENT3); +AuRWLockFuncs(new_child, NEW_CHILD); + +#undef AuReadLockFunc +#undef AuWriteLockFunc +#undef AuRWLockFuncs + +#define ii_read_unlock(i) au_rw_read_unlock(&au_ii(i)->ii_rwsem) +#define ii_write_unlock(i) au_rw_write_unlock(&au_ii(i)->ii_rwsem) +#define ii_downgrade_lock(i) au_rw_dgrade_lock(&au_ii(i)->ii_rwsem) + +#define IiMustNoWaiters(i) AuRwMustNoWaiters(&au_ii(i)->ii_rwsem) +#define IiMustAnyLock(i) AuRwMustAnyLock(&au_ii(i)->ii_rwsem) +#define IiMustWriteLock(i) AuRwMustWriteLock(&au_ii(i)->ii_rwsem) + +/* ---------------------------------------------------------------------- */ + +static inline void au_icntnr_init(struct au_icntnr *c) +{ +#ifdef CONFIG_AUFS_DEBUG + c->vfs_inode.i_mode = 0; +#endif +} + +static inline unsigned int au_iigen(struct inode *inode, unsigned int *igflags) +{ + unsigned int gen; + struct au_iinfo *iinfo; + struct au_iigen *iigen; + + iinfo = au_ii(inode); + iigen = &iinfo->ii_generation; + spin_lock(&iigen->ig_spin); + if (igflags) + *igflags = iigen->ig_flags; + gen = iigen->ig_generation; + spin_unlock(&iigen->ig_spin); + + return gen; +} + +/* tiny test for inode number */ +/* tmpfs generation is too rough */ +static inline int au_test_higen(struct inode *inode, struct inode *h_inode) +{ + struct au_iinfo *iinfo; + + iinfo = au_ii(inode); + AuRwMustAnyLock(&iinfo->ii_rwsem); + return !(iinfo->ii_hsb1 == h_inode->i_sb + && iinfo->ii_higen == h_inode->i_generation); +} + +static inline void au_iigen_dec(struct inode *inode) +{ + struct au_iinfo *iinfo; + struct au_iigen *iigen; + + iinfo = au_ii(inode); + iigen = &iinfo->ii_generation; + spin_lock(&iigen->ig_spin); + iigen->ig_generation--; + spin_unlock(&iigen->ig_spin); +} + +static inline int au_iigen_test(struct inode *inode, unsigned int sigen) +{ + int err; + + err = 0; + if (unlikely(inode && au_iigen(inode, NULL) != sigen)) + err = -EIO; + + return err; +} + +/* ---------------------------------------------------------------------- */ + +static inline struct au_hinode *au_hinode(struct au_iinfo *iinfo, + aufs_bindex_t bindex) +{ + return iinfo->ii_hinode + bindex; +} + +static inline int au_is_bad_inode(struct inode *inode) +{ + return !!(is_bad_inode(inode) || !au_hinode(au_ii(inode), 0)); +} + +static inline aufs_bindex_t au_ii_br_id(struct inode *inode, + aufs_bindex_t bindex) +{ + IiMustAnyLock(inode); + return au_hinode(au_ii(inode), bindex)->hi_id; +} + +static inline aufs_bindex_t au_ibtop(struct inode *inode) +{ + IiMustAnyLock(inode); + return au_ii(inode)->ii_btop; +} + +static inline aufs_bindex_t au_ibbot(struct inode *inode) +{ + IiMustAnyLock(inode); + return au_ii(inode)->ii_bbot; +} + +static inline struct au_vdir *au_ivdir(struct inode *inode) +{ + IiMustAnyLock(inode); + return au_ii(inode)->ii_vdir; +} + +static inline struct dentry *au_hi_wh(struct inode *inode, aufs_bindex_t bindex) +{ + IiMustAnyLock(inode); + return au_hinode(au_ii(inode), bindex)->hi_whdentry; +} + +static inline void au_set_ibtop(struct inode *inode, aufs_bindex_t bindex) +{ + IiMustWriteLock(inode); + au_ii(inode)->ii_btop = bindex; +} + +static inline void au_set_ibbot(struct inode *inode, aufs_bindex_t bindex) +{ + IiMustWriteLock(inode); + au_ii(inode)->ii_bbot = bindex; +} + +static inline void au_set_ivdir(struct inode *inode, struct au_vdir *vdir) +{ + IiMustWriteLock(inode); + au_ii(inode)->ii_vdir = vdir; +} + +static inline struct au_hinode *au_hi(struct inode *inode, aufs_bindex_t bindex) +{ + IiMustAnyLock(inode); + return au_hinode(au_ii(inode), bindex); +} + +/* ---------------------------------------------------------------------- */ + +static inline struct dentry *au_pinned_parent(struct au_pin *pin) +{ + if (pin) + return pin->parent; + return NULL; +} + +static inline struct inode *au_pinned_h_dir(struct au_pin *pin) +{ + if (pin && pin->hdir) + return pin->hdir->hi_inode; + return NULL; +} + +static inline struct au_hinode *au_pinned_hdir(struct au_pin *pin) +{ + if (pin) + return pin->hdir; + return NULL; +} + +static inline void au_pin_set_dentry(struct au_pin *pin, struct dentry *dentry) +{ + if (pin) + pin->dentry = dentry; +} + +static inline void au_pin_set_parent_lflag(struct au_pin *pin, + unsigned char lflag) +{ + if (pin) { + if (lflag) + au_fset_pin(pin->flags, DI_LOCKED); + else + au_fclr_pin(pin->flags, DI_LOCKED); + } +} + +#if 0 /* reserved */ +static inline void au_pin_set_parent(struct au_pin *pin, struct dentry *parent) +{ + if (pin) { + dput(pin->parent); + pin->parent = dget(parent); + } +} +#endif + +/* ---------------------------------------------------------------------- */ + +struct au_branch; +#ifdef CONFIG_AUFS_HNOTIFY +struct au_hnotify_op { + void (*ctl)(struct au_hinode *hinode, int do_set); + int (*alloc)(struct au_hinode *hinode); + + /* + * if it returns true, the caller should free hinode->hi_notify, + * otherwise ->free() frees it. + */ + int (*free)(struct au_hinode *hinode, + struct au_hnotify *hn) __must_check; + + void (*fin)(void); + int (*init)(void); + + int (*reset_br)(unsigned int udba, struct au_branch *br, int perm); + void (*fin_br)(struct au_branch *br); + int (*init_br)(struct au_branch *br, int perm); +}; + +/* hnotify.c */ +int au_hn_alloc(struct au_hinode *hinode, struct inode *inode); +void au_hn_free(struct au_hinode *hinode); +void au_hn_ctl(struct au_hinode *hinode, int do_set); +void au_hn_reset(struct inode *inode, unsigned int flags); +int au_hnotify(struct inode *h_dir, struct au_hnotify *hnotify, u32 mask, + const struct qstr *h_child_qstr, struct inode *h_child_inode); +int au_hnotify_reset_br(unsigned int udba, struct au_branch *br, int perm); +int au_hnotify_init_br(struct au_branch *br, int perm); +void au_hnotify_fin_br(struct au_branch *br); +int __init au_hnotify_init(void); +void au_hnotify_fin(void); + +/* hfsnotify.c */ +extern const struct au_hnotify_op au_hnotify_op; + +static inline +void au_hn_init(struct au_hinode *hinode) +{ + hinode->hi_notify = NULL; +} + +static inline struct au_hnotify *au_hn(struct au_hinode *hinode) +{ + return hinode->hi_notify; +} + +#else +AuStub(int, au_hn_alloc, return -EOPNOTSUPP, + struct au_hinode *hinode __maybe_unused, + struct inode *inode __maybe_unused) +AuStub(struct au_hnotify *, au_hn, return NULL, struct au_hinode *hinode) +AuStubVoid(au_hn_free, struct au_hinode *hinode __maybe_unused) +AuStubVoid(au_hn_ctl, struct au_hinode *hinode __maybe_unused, + int do_set __maybe_unused) +AuStubVoid(au_hn_reset, struct inode *inode __maybe_unused, + unsigned int flags __maybe_unused) +AuStubInt0(au_hnotify_reset_br, unsigned int udba __maybe_unused, + struct au_branch *br __maybe_unused, + int perm __maybe_unused) +AuStubInt0(au_hnotify_init_br, struct au_branch *br __maybe_unused, + int perm __maybe_unused) +AuStubVoid(au_hnotify_fin_br, struct au_branch *br __maybe_unused) +AuStubInt0(__init au_hnotify_init, void) +AuStubVoid(au_hnotify_fin, void) +AuStubVoid(au_hn_init, struct au_hinode *hinode __maybe_unused) +#endif /* CONFIG_AUFS_HNOTIFY */ + +static inline void au_hn_suspend(struct au_hinode *hdir) +{ + au_hn_ctl(hdir, /*do_set*/0); +} + +static inline void au_hn_resume(struct au_hinode *hdir) +{ + au_hn_ctl(hdir, /*do_set*/1); +} + +static inline void au_hn_inode_lock(struct au_hinode *hdir) +{ + inode_lock(hdir->hi_inode); + au_hn_suspend(hdir); +} + +static inline void au_hn_inode_lock_nested(struct au_hinode *hdir, + unsigned int sc __maybe_unused) +{ + inode_lock_nested(hdir->hi_inode, sc); + au_hn_suspend(hdir); +} + +#if 0 /* unused */ +#include "vfsub.h" +static inline void au_hn_inode_lock_shared_nested(struct au_hinode *hdir, + unsigned int sc) +{ + inode_lock_shared_nested(hdir->hi_inode, sc); + au_hn_suspend(hdir); +} +#endif + +static inline void au_hn_inode_unlock(struct au_hinode *hdir) +{ + au_hn_resume(hdir); + inode_unlock(hdir->hi_inode); +} + +#endif /* __KERNEL__ */ +#endif /* __AUFS_INODE_H__ */ diff --git a/fs/aufs/ioctl.c b/fs/aufs/ioctl.c new file mode 100644 index 0000000000000..67128f425fc43 --- /dev/null +++ b/fs/aufs/ioctl.c @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * ioctl + * plink-management and readdir in userspace. + * assist the pathconf(3) wrapper library. + * move-down + * File-based Hierarchical Storage Management. + */ + +#include +#include +#include "aufs.h" + +static int au_wbr_fd(struct path *path, struct aufs_wbr_fd __user *arg) +{ + int err, fd; + aufs_bindex_t wbi, bindex, bbot; + struct file *h_file; + struct super_block *sb; + struct dentry *root; + struct au_branch *br; + struct aufs_wbr_fd wbrfd = { + .oflags = au_dir_roflags, + .brid = -1 + }; + const int valid = O_RDONLY | O_NONBLOCK | O_LARGEFILE | O_DIRECTORY + | O_NOATIME | O_CLOEXEC; + + AuDebugOn(wbrfd.oflags & ~valid); + + if (arg) { + err = copy_from_user(&wbrfd, arg, sizeof(wbrfd)); + if (unlikely(err)) { + err = -EFAULT; + goto out; + } + + err = -EINVAL; + AuDbg("wbrfd{0%o, %d}\n", wbrfd.oflags, wbrfd.brid); + wbrfd.oflags |= au_dir_roflags; + AuDbg("0%o\n", wbrfd.oflags); + if (unlikely(wbrfd.oflags & ~valid)) + goto out; + } + + fd = get_unused_fd_flags(0); + err = fd; + if (unlikely(fd < 0)) + goto out; + + h_file = ERR_PTR(-EINVAL); + wbi = 0; + br = NULL; + sb = path->dentry->d_sb; + root = sb->s_root; + aufs_read_lock(root, AuLock_IR); + bbot = au_sbbot(sb); + if (wbrfd.brid >= 0) { + wbi = au_br_index(sb, wbrfd.brid); + if (unlikely(wbi < 0 || wbi > bbot)) + goto out_unlock; + } + + h_file = ERR_PTR(-ENOENT); + br = au_sbr(sb, wbi); + if (!au_br_writable(br->br_perm)) { + if (arg) + goto out_unlock; + + bindex = wbi + 1; + wbi = -1; + for (; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + if (au_br_writable(br->br_perm)) { + wbi = bindex; + br = au_sbr(sb, wbi); + break; + } + } + } + AuDbg("wbi %d\n", wbi); + if (wbi >= 0) + h_file = au_h_open(root, wbi, wbrfd.oflags, NULL, + /*force_wr*/0); + +out_unlock: + aufs_read_unlock(root, AuLock_IR); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out_fd; + + au_lcnt_dec(&br->br_nfiles); /* cf. au_h_open() */ + fd_install(fd, h_file); + err = fd; + goto out; /* success */ + +out_fd: + put_unused_fd(fd); +out: + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg) +{ + long err; + struct dentry *dentry; + + switch (cmd) { + case AUFS_CTL_RDU: + case AUFS_CTL_RDU_INO: + err = au_rdu_ioctl(file, cmd, arg); + break; + + case AUFS_CTL_WBR_FD: + err = au_wbr_fd(&file->f_path, (void __user *)arg); + break; + + case AUFS_CTL_IBUSY: + err = au_ibusy_ioctl(file, arg); + break; + + case AUFS_CTL_BRINFO: + err = au_brinfo_ioctl(file, arg); + break; + + case AUFS_CTL_FHSM_FD: + dentry = file->f_path.dentry; + if (IS_ROOT(dentry)) + err = au_fhsm_fd(dentry->d_sb, arg); + else + err = -ENOTTY; + break; + + default: + /* do not call the lower */ + AuDbg("0x%x\n", cmd); + err = -ENOTTY; + } + + AuTraceErr(err); + return err; +} + +long aufs_ioctl_nondir(struct file *file, unsigned int cmd, unsigned long arg) +{ + long err; + + switch (cmd) { + case AUFS_CTL_MVDOWN: + err = au_mvdown(file->f_path.dentry, (void __user *)arg); + break; + + case AUFS_CTL_WBR_FD: + err = au_wbr_fd(&file->f_path, (void __user *)arg); + break; + + default: + /* do not call the lower */ + AuDbg("0x%x\n", cmd); + err = -ENOTTY; + } + + AuTraceErr(err); + return err; +} + +#ifdef CONFIG_COMPAT +long aufs_compat_ioctl_dir(struct file *file, unsigned int cmd, + unsigned long arg) +{ + long err; + + switch (cmd) { + case AUFS_CTL_RDU: + case AUFS_CTL_RDU_INO: + err = au_rdu_compat_ioctl(file, cmd, arg); + break; + + case AUFS_CTL_IBUSY: + err = au_ibusy_compat_ioctl(file, arg); + break; + + case AUFS_CTL_BRINFO: + err = au_brinfo_compat_ioctl(file, arg); + break; + + default: + err = aufs_ioctl_dir(file, cmd, arg); + } + + AuTraceErr(err); + return err; +} + +long aufs_compat_ioctl_nondir(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return aufs_ioctl_nondir(file, cmd, (unsigned long)compat_ptr(arg)); +} +#endif diff --git a/fs/aufs/lcnt.h b/fs/aufs/lcnt.h new file mode 100644 index 0000000000000..4ee7bec9dca31 --- /dev/null +++ b/fs/aufs/lcnt.h @@ -0,0 +1,186 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * simple long counter wrapper + */ + +#ifndef __AUFS_LCNT_H__ +#define __AUFS_LCNT_H__ + +#ifdef __KERNEL__ + +#include "debug.h" + +#define AuLCntATOMIC 1 +#define AuLCntPCPUCNT 2 +/* + * why does percpu_refcount require extra synchronize_rcu()s in + * au_br_do_free() + */ +#define AuLCntPCPUREF 3 + +/* #define AuLCntChosen AuLCntATOMIC */ +#define AuLCntChosen AuLCntPCPUCNT +/* #define AuLCntChosen AuLCntPCPUREF */ + +#if AuLCntChosen == AuLCntATOMIC +#include + +typedef atomic_long_t au_lcnt_t; + +static inline int au_lcnt_init(au_lcnt_t *cnt, void *release __maybe_unused) +{ + atomic_long_set(cnt, 0); + return 0; +} + +static inline void au_lcnt_wait_for_fin(au_lcnt_t *cnt __maybe_unused) +{ + /* empty */ +} + +static inline void au_lcnt_fin(au_lcnt_t *cnt __maybe_unused, + int do_sync __maybe_unused) +{ + /* empty */ +} + +static inline void au_lcnt_inc(au_lcnt_t *cnt) +{ + atomic_long_inc(cnt); +} + +static inline void au_lcnt_dec(au_lcnt_t *cnt) +{ + atomic_long_dec(cnt); +} + +static inline long au_lcnt_read(au_lcnt_t *cnt, int do_rev __maybe_unused) +{ + return atomic_long_read(cnt); +} +#endif + +#if AuLCntChosen == AuLCntPCPUCNT +#include + +typedef struct percpu_counter au_lcnt_t; + +static inline int au_lcnt_init(au_lcnt_t *cnt, void *release __maybe_unused) +{ + return percpu_counter_init(cnt, 0, GFP_NOFS); +} + +static inline void au_lcnt_wait_for_fin(au_lcnt_t *cnt __maybe_unused) +{ + /* empty */ +} + +static inline void au_lcnt_fin(au_lcnt_t *cnt, int do_sync __maybe_unused) +{ + percpu_counter_destroy(cnt); +} + +static inline void au_lcnt_inc(au_lcnt_t *cnt) +{ + percpu_counter_inc(cnt); +} + +static inline void au_lcnt_dec(au_lcnt_t *cnt) +{ + percpu_counter_dec(cnt); +} + +static inline long au_lcnt_read(au_lcnt_t *cnt, int do_rev __maybe_unused) +{ + s64 n; + + n = percpu_counter_sum(cnt); + BUG_ON(n < 0); + if (LONG_MAX != LLONG_MAX + && n > LONG_MAX) + AuWarn1("%s\n", "wrap-around"); + + return n; +} +#endif + +#if AuLCntChosen == AuLCntPCPUREF +#include + +typedef struct percpu_ref au_lcnt_t; + +static inline int au_lcnt_init(au_lcnt_t *cnt, percpu_ref_func_t *release) +{ + if (!release) + release = percpu_ref_exit; + return percpu_ref_init(cnt, release, /*percpu mode*/0, GFP_NOFS); +} + +static inline void au_lcnt_wait_for_fin(au_lcnt_t *cnt __maybe_unused) +{ + synchronize_rcu(); +} + +static inline void au_lcnt_fin(au_lcnt_t *cnt, int do_sync) +{ + percpu_ref_kill(cnt); + if (do_sync) + au_lcnt_wait_for_fin(cnt); +} + +static inline void au_lcnt_inc(au_lcnt_t *cnt) +{ + percpu_ref_get(cnt); +} + +static inline void au_lcnt_dec(au_lcnt_t *cnt) +{ + percpu_ref_put(cnt); +} + +/* + * avoid calling this func as possible. + */ +static inline long au_lcnt_read(au_lcnt_t *cnt, int do_rev) +{ + long l; + + percpu_ref_switch_to_atomic_sync(cnt); + l = atomic_long_read(&cnt->count); + if (do_rev) + percpu_ref_switch_to_percpu(cnt); + + /* percpu_ref is initialized by 1 instead of 0 */ + return l - 1; +} +#endif + +#ifdef CONFIG_AUFS_DEBUG +#define AuLCntZero(val) do { \ + long l = val; \ + if (l) \ + AuDbg("%s = %ld\n", #val, l); \ +} while (0) +#else +#define AuLCntZero(val) do {} while (0) +#endif + +#endif /* __KERNEL__ */ +#endif /* __AUFS_LCNT_H__ */ diff --git a/fs/aufs/loop.c b/fs/aufs/loop.c new file mode 100644 index 0000000000000..588681413eb3a --- /dev/null +++ b/fs/aufs/loop.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * support for loopback block device as a branch + */ + +#include "aufs.h" + +/* added into drivers/block/loop.c */ +static struct file *(*backing_file_func)(struct super_block *sb); + +/* + * test if two lower dentries have overlapping branches. + */ +int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_adding) +{ + struct super_block *h_sb; + struct file *backing_file; + + if (unlikely(!backing_file_func)) { + /* don't load "loop" module here */ + backing_file_func = symbol_get(loop_backing_file); + if (unlikely(!backing_file_func)) + /* "loop" module is not loaded */ + return 0; + } + + h_sb = h_adding->d_sb; + backing_file = backing_file_func(h_sb); + if (!backing_file) + return 0; + + h_adding = backing_file->f_path.dentry; + /* + * h_adding can be local NFS. + * in this case aufs cannot detect the loop. + */ + if (unlikely(h_adding->d_sb == sb)) + return 1; + return !!au_test_subdir(h_adding, sb->s_root); +} + +/* true if a kernel thread named 'loop[0-9].*' accesses a file */ +int au_test_loopback_kthread(void) +{ + int ret; + struct task_struct *tsk = current; + char c, comm[sizeof(tsk->comm)]; + + ret = 0; + if (tsk->flags & PF_KTHREAD) { + get_task_comm(comm, tsk); + c = comm[4]; + ret = ('0' <= c && c <= '9' + && !strncmp(comm, "loop", 4)); + } + + return ret; +} + +/* ---------------------------------------------------------------------- */ + +#define au_warn_loopback_step 16 +static int au_warn_loopback_nelem = au_warn_loopback_step; +static unsigned long *au_warn_loopback_array; + +void au_warn_loopback(struct super_block *h_sb) +{ + int i, new_nelem; + unsigned long *a, magic; + static DEFINE_SPINLOCK(spin); + + magic = h_sb->s_magic; + spin_lock(&spin); + a = au_warn_loopback_array; + for (i = 0; i < au_warn_loopback_nelem && *a; i++) + if (a[i] == magic) { + spin_unlock(&spin); + return; + } + + /* h_sb is new to us, print it */ + if (i < au_warn_loopback_nelem) { + a[i] = magic; + goto pr; + } + + /* expand the array */ + new_nelem = au_warn_loopback_nelem + au_warn_loopback_step; + a = au_kzrealloc(au_warn_loopback_array, + au_warn_loopback_nelem * sizeof(unsigned long), + new_nelem * sizeof(unsigned long), GFP_ATOMIC, + /*may_shrink*/0); + if (a) { + au_warn_loopback_nelem = new_nelem; + au_warn_loopback_array = a; + a[i] = magic; + goto pr; + } + + spin_unlock(&spin); + AuWarn1("realloc failed, ignored\n"); + return; + +pr: + spin_unlock(&spin); + pr_warn("you may want to try another patch for loopback file " + "on %s(0x%lx) branch\n", au_sbtype(h_sb), magic); +} + +int au_loopback_init(void) +{ + int err; + struct super_block *sb __maybe_unused; + + BUILD_BUG_ON(sizeof(sb->s_magic) != sizeof(*au_warn_loopback_array)); + + err = 0; + au_warn_loopback_array = kcalloc(au_warn_loopback_step, + sizeof(unsigned long), GFP_NOFS); + if (unlikely(!au_warn_loopback_array)) + err = -ENOMEM; + + return err; +} + +void au_loopback_fin(void) +{ + if (backing_file_func) + symbol_put(loop_backing_file); + au_kfree_try_rcu(au_warn_loopback_array); +} diff --git a/fs/aufs/loop.h b/fs/aufs/loop.h new file mode 100644 index 0000000000000..b87ba6e1ee9d2 --- /dev/null +++ b/fs/aufs/loop.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * support for loopback mount as a branch + */ + +#ifndef __AUFS_LOOP_H__ +#define __AUFS_LOOP_H__ + +#ifdef __KERNEL__ + +struct dentry; +struct super_block; + +#ifdef CONFIG_AUFS_BDEV_LOOP +/* drivers/block/loop.c */ +struct file *loop_backing_file(struct super_block *sb); + +/* loop.c */ +int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_adding); +int au_test_loopback_kthread(void); +void au_warn_loopback(struct super_block *h_sb); + +int au_loopback_init(void); +void au_loopback_fin(void); +#else +AuStub(struct file *, loop_backing_file, return NULL, struct super_block *sb) + +AuStubInt0(au_test_loopback_overlap, struct super_block *sb, + struct dentry *h_adding) +AuStubInt0(au_test_loopback_kthread, void) +AuStubVoid(au_warn_loopback, struct super_block *h_sb) + +AuStubInt0(au_loopback_init, void) +AuStubVoid(au_loopback_fin, void) +#endif /* BLK_DEV_LOOP */ + +#endif /* __KERNEL__ */ +#endif /* __AUFS_LOOP_H__ */ diff --git a/fs/aufs/magic.mk b/fs/aufs/magic.mk new file mode 100644 index 0000000000000..7bc9eef3ffec5 --- /dev/null +++ b/fs/aufs/magic.mk @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: GPL-2.0 + +# defined in ${srctree}/fs/fuse/inode.c +# tristate +ifdef CONFIG_FUSE_FS +ccflags-y += -DFUSE_SUPER_MAGIC=0x65735546 +endif + +# defined in ${srctree}/fs/xfs/xfs_sb.h +# tristate +ifdef CONFIG_XFS_FS +ccflags-y += -DXFS_SB_MAGIC=0x58465342 +endif + +# defined in ${srctree}/fs/configfs/mount.c +# tristate +ifdef CONFIG_CONFIGFS_FS +ccflags-y += -DCONFIGFS_MAGIC=0x62656570 +endif + +# defined in ${srctree}/fs/ubifs/ubifs.h +# tristate +ifdef CONFIG_UBIFS_FS +ccflags-y += -DUBIFS_SUPER_MAGIC=0x24051905 +endif + +# defined in ${srctree}/fs/hfsplus/hfsplus_raw.h +# tristate +ifdef CONFIG_HFSPLUS_FS +ccflags-y += -DHFSPLUS_SUPER_MAGIC=0x482b +endif diff --git a/fs/aufs/module.c b/fs/aufs/module.c new file mode 100644 index 0000000000000..0d8c78c942645 --- /dev/null +++ b/fs/aufs/module.c @@ -0,0 +1,273 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * module global variables and operations + */ + +#include +#include +#include "aufs.h" + +/* shrinkable realloc */ +void *au_krealloc(void *p, unsigned int new_sz, gfp_t gfp, int may_shrink) +{ + size_t sz; + int diff; + + sz = 0; + diff = -1; + if (p) { +#if 0 /* unused */ + if (!new_sz) { + au_kfree_rcu(p); + p = NULL; + goto out; + } +#else + AuDebugOn(!new_sz); +#endif + sz = ksize(p); + diff = au_kmidx_sub(sz, new_sz); + } + if (sz && !diff) + goto out; + + if (sz < new_sz) + /* expand or SLOB */ + p = krealloc(p, new_sz, gfp); + else if (new_sz < sz && may_shrink) { + /* shrink */ + void *q; + + q = kmalloc(new_sz, gfp); + if (q) { + if (p) { + memcpy(q, p, new_sz); + au_kfree_try_rcu(p); + } + p = q; + } else + p = NULL; + } + +out: + return p; +} + +void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp, + int may_shrink) +{ + p = au_krealloc(p, new_sz, gfp, may_shrink); + if (p && new_sz > nused) + memset(p + nused, 0, new_sz - nused); + return p; +} + +/* ---------------------------------------------------------------------- */ +/* + * aufs caches + */ +struct kmem_cache *au_cache[AuCache_Last]; + +static void au_cache_fin(void) +{ + int i; + + /* + * Make sure all delayed rcu free inodes are flushed before we + * destroy cache. + */ + rcu_barrier(); + + /* excluding AuCache_HNOTIFY */ + BUILD_BUG_ON(AuCache_HNOTIFY + 1 != AuCache_Last); + for (i = 0; i < AuCache_HNOTIFY; i++) { + kmem_cache_destroy(au_cache[i]); + au_cache[i] = NULL; + } +} + +static int __init au_cache_init(void) +{ + au_cache[AuCache_DINFO] = AuCacheCtor(au_dinfo, au_di_init_once); + if (au_cache[AuCache_DINFO]) + /* SLAB_DESTROY_BY_RCU */ + au_cache[AuCache_ICNTNR] = AuCacheCtor(au_icntnr, + au_icntnr_init_once); + if (au_cache[AuCache_ICNTNR]) + au_cache[AuCache_FINFO] = AuCacheCtor(au_finfo, + au_fi_init_once); + if (au_cache[AuCache_FINFO]) + au_cache[AuCache_VDIR] = AuCache(au_vdir); + if (au_cache[AuCache_VDIR]) + au_cache[AuCache_DEHSTR] = AuCache(au_vdir_dehstr); + if (au_cache[AuCache_DEHSTR]) + return 0; + + au_cache_fin(); + return -ENOMEM; +} + +/* ---------------------------------------------------------------------- */ + +int au_dir_roflags; + +#ifdef CONFIG_AUFS_SBILIST +/* + * iterate_supers_type() doesn't protect us from + * remounting (branch management) + */ +struct hlist_bl_head au_sbilist; +#endif + +/* + * functions for module interface. + */ +MODULE_LICENSE("GPL"); +/* MODULE_LICENSE("GPL v2"); */ +MODULE_AUTHOR("Junjiro R. Okajima "); +MODULE_DESCRIPTION(AUFS_NAME + " -- Advanced multi layered unification filesystem"); +MODULE_VERSION(AUFS_VERSION); +MODULE_ALIAS_FS(AUFS_NAME); + +/* this module parameter has no meaning when SYSFS is disabled */ +int sysaufs_brs = 1; +MODULE_PARM_DESC(brs, "use /fs/aufs/si_*/brN"); +module_param_named(brs, sysaufs_brs, int, 0444); + +/* this module parameter has no meaning when USER_NS is disabled */ +bool au_userns; +MODULE_PARM_DESC(allow_userns, "allow unprivileged to mount under userns"); +module_param_named(allow_userns, au_userns, bool, 0444); + +/* ---------------------------------------------------------------------- */ + +static char au_esc_chars[0x20 + 3]; /* 0x01-0x20, backslash, del, and NULL */ + +int au_seq_path(struct seq_file *seq, struct path *path) +{ + int err; + + err = seq_path(seq, path, au_esc_chars); + if (err >= 0) + err = 0; + else + err = -ENOMEM; + + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int __init aufs_init(void) +{ + int err, i; + char *p; + + p = au_esc_chars; + for (i = 1; i <= ' '; i++) + *p++ = i; + *p++ = '\\'; + *p++ = '\x7f'; + *p = 0; + + au_dir_roflags = au_file_roflags(O_DIRECTORY | O_LARGEFILE); + + memcpy(aufs_iop_nogetattr, aufs_iop, sizeof(aufs_iop)); + for (i = 0; i < AuIop_Last; i++) + aufs_iop_nogetattr[i].getattr = NULL; + + memset(au_cache, 0, sizeof(au_cache)); /* including hnotify */ + + au_sbilist_init(); + sysaufs_brs_init(); + au_debug_init(); + au_dy_init(); + err = sysaufs_init(); + if (unlikely(err)) + goto out; + err = dbgaufs_init(); + if (unlikely(err)) + goto out_sysaufs; + err = au_procfs_init(); + if (unlikely(err)) + goto out_dbgaufs; + err = au_wkq_init(); + if (unlikely(err)) + goto out_procfs; + err = au_loopback_init(); + if (unlikely(err)) + goto out_wkq; + err = au_hnotify_init(); + if (unlikely(err)) + goto out_loopback; + err = au_sysrq_init(); + if (unlikely(err)) + goto out_hin; + err = au_cache_init(); + if (unlikely(err)) + goto out_sysrq; + + aufs_fs_type.fs_flags |= au_userns ? FS_USERNS_MOUNT : 0; + err = register_filesystem(&aufs_fs_type); + if (unlikely(err)) + goto out_cache; + + /* since we define pr_fmt, call printk directly */ + printk(KERN_INFO AUFS_NAME " " AUFS_VERSION "\n"); + goto out; /* success */ + +out_cache: + au_cache_fin(); +out_sysrq: + au_sysrq_fin(); +out_hin: + au_hnotify_fin(); +out_loopback: + au_loopback_fin(); +out_wkq: + au_wkq_fin(); +out_procfs: + au_procfs_fin(); +out_dbgaufs: + dbgaufs_fin(); +out_sysaufs: + sysaufs_fin(); + au_dy_fin(); +out: + return err; +} + +static void __exit aufs_exit(void) +{ + unregister_filesystem(&aufs_fs_type); + au_cache_fin(); + au_sysrq_fin(); + au_hnotify_fin(); + au_loopback_fin(); + au_wkq_fin(); + au_procfs_fin(); + dbgaufs_fin(); + sysaufs_fin(); + au_dy_fin(); +} + +module_init(aufs_init); +module_exit(aufs_exit); diff --git a/fs/aufs/module.h b/fs/aufs/module.h new file mode 100644 index 0000000000000..16e0ad9774a49 --- /dev/null +++ b/fs/aufs/module.h @@ -0,0 +1,166 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * module initialization and module-global + */ + +#ifndef __AUFS_MODULE_H__ +#define __AUFS_MODULE_H__ + +#ifdef __KERNEL__ + +#include +#include "debug.h" +#include "dentry.h" +#include "dir.h" +#include "file.h" +#include "inode.h" + +struct path; +struct seq_file; + +/* module parameters */ +extern int sysaufs_brs; +extern bool au_userns; + +/* ---------------------------------------------------------------------- */ + +extern int au_dir_roflags; + +void *au_krealloc(void *p, unsigned int new_sz, gfp_t gfp, int may_shrink); +void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp, + int may_shrink); + +/* + * Comparing the size of the object with sizeof(struct rcu_head) + * case 1: object is always larger + * --> au_kfree_rcu() or au_kfree_do_rcu() + * case 2: object is always smaller + * --> au_kfree_small() + * case 3: object can be any size + * --> au_kfree_try_rcu() + */ + +static inline void au_kfree_do_rcu(const void *p) +{ + struct { + struct rcu_head rcu; + } *a = (void *)p; + + kfree_rcu(a, rcu); +} + +#define au_kfree_rcu(_p) do { \ + typeof(_p) p = (_p); \ + BUILD_BUG_ON(sizeof(*p) < sizeof(struct rcu_head)); \ + if (p) \ + au_kfree_do_rcu(p); \ + } while (0) + +#define au_kfree_do_sz_test(sz) (sz >= sizeof(struct rcu_head)) +#define au_kfree_sz_test(p) (p && au_kfree_do_sz_test(ksize(p))) + +static inline void au_kfree_try_rcu(const void *p) +{ + if (!p) + return; + if (au_kfree_sz_test(p)) + au_kfree_do_rcu(p); + else + kfree(p); +} + +static inline void au_kfree_small(const void *p) +{ + if (!p) + return; + AuDebugOn(au_kfree_sz_test(p)); + kfree(p); +} + +static inline int au_kmidx_sub(size_t sz, size_t new_sz) +{ +#ifndef CONFIG_SLOB + return kmalloc_index(sz) - kmalloc_index(new_sz); +#else + return -1; /* SLOB is untested */ +#endif +} + +int au_seq_path(struct seq_file *seq, struct path *path); + +#ifdef CONFIG_PROC_FS +/* procfs.c */ +int __init au_procfs_init(void); +void au_procfs_fin(void); +#else +AuStubInt0(au_procfs_init, void); +AuStubVoid(au_procfs_fin, void); +#endif + +/* ---------------------------------------------------------------------- */ + +/* kmem cache */ +enum { + AuCache_DINFO, + AuCache_ICNTNR, + AuCache_FINFO, + AuCache_VDIR, + AuCache_DEHSTR, + AuCache_HNOTIFY, /* must be last */ + AuCache_Last +}; + +extern struct kmem_cache *au_cache[AuCache_Last]; + +#define AuCacheFlags (SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD) +#define AuCache(type) KMEM_CACHE(type, AuCacheFlags) +#define AuCacheCtor(type, ctor) \ + kmem_cache_create(#type, sizeof(struct type), \ + __alignof__(struct type), AuCacheFlags, ctor) + +#define AuCacheFuncs(name, index) \ + static inline struct au_##name *au_cache_alloc_##name(void) \ + { return kmem_cache_alloc(au_cache[AuCache_##index], GFP_NOFS); } \ + static inline void au_cache_free_##name##_norcu(struct au_##name *p) \ + { kmem_cache_free(au_cache[AuCache_##index], p); } \ + \ + static inline void au_cache_free_##name##_rcu_cb(struct rcu_head *rcu) \ + { void *p = rcu; \ + p -= offsetof(struct au_##name, rcu); \ + kmem_cache_free(au_cache[AuCache_##index], p); } \ + static inline void au_cache_free_##name##_rcu(struct au_##name *p) \ + { BUILD_BUG_ON(sizeof(struct au_##name) < sizeof(struct rcu_head)); \ + call_rcu(&p->rcu, au_cache_free_##name##_rcu_cb); } \ + \ + static inline void au_cache_free_##name(struct au_##name *p) \ + { /* au_cache_free_##name##_norcu(p); */ \ + au_cache_free_##name##_rcu(p); } + +AuCacheFuncs(dinfo, DINFO); +AuCacheFuncs(icntnr, ICNTNR); +AuCacheFuncs(finfo, FINFO); +AuCacheFuncs(vdir, VDIR); +AuCacheFuncs(vdir_dehstr, DEHSTR); +#ifdef CONFIG_AUFS_HNOTIFY +AuCacheFuncs(hnotify, HNOTIFY); +#endif + +#endif /* __KERNEL__ */ +#endif /* __AUFS_MODULE_H__ */ diff --git a/fs/aufs/mvdown.c b/fs/aufs/mvdown.c new file mode 100644 index 0000000000000..26f643adcdb28 --- /dev/null +++ b/fs/aufs/mvdown.c @@ -0,0 +1,706 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2011-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * move-down, opposite of copy-up + */ + +#include "aufs.h" + +struct au_mvd_args { + struct { + struct super_block *h_sb; + struct dentry *h_parent; + struct au_hinode *hdir; + struct inode *h_dir, *h_inode; + struct au_pin pin; + } info[AUFS_MVDOWN_NARRAY]; + + struct aufs_mvdown mvdown; + struct dentry *dentry, *parent; + struct inode *inode, *dir; + struct super_block *sb; + aufs_bindex_t bopq, bwh, bfound; + unsigned char rename_lock; +}; + +#define mvd_errno mvdown.au_errno +#define mvd_bsrc mvdown.stbr[AUFS_MVDOWN_UPPER].bindex +#define mvd_src_brid mvdown.stbr[AUFS_MVDOWN_UPPER].brid +#define mvd_bdst mvdown.stbr[AUFS_MVDOWN_LOWER].bindex +#define mvd_dst_brid mvdown.stbr[AUFS_MVDOWN_LOWER].brid + +#define mvd_h_src_sb info[AUFS_MVDOWN_UPPER].h_sb +#define mvd_h_src_parent info[AUFS_MVDOWN_UPPER].h_parent +#define mvd_hdir_src info[AUFS_MVDOWN_UPPER].hdir +#define mvd_h_src_dir info[AUFS_MVDOWN_UPPER].h_dir +#define mvd_h_src_inode info[AUFS_MVDOWN_UPPER].h_inode +#define mvd_pin_src info[AUFS_MVDOWN_UPPER].pin + +#define mvd_h_dst_sb info[AUFS_MVDOWN_LOWER].h_sb +#define mvd_h_dst_parent info[AUFS_MVDOWN_LOWER].h_parent +#define mvd_hdir_dst info[AUFS_MVDOWN_LOWER].hdir +#define mvd_h_dst_dir info[AUFS_MVDOWN_LOWER].h_dir +#define mvd_h_dst_inode info[AUFS_MVDOWN_LOWER].h_inode +#define mvd_pin_dst info[AUFS_MVDOWN_LOWER].pin + +#define AU_MVD_PR(flag, ...) do { \ + if (flag) \ + pr_err(__VA_ARGS__); \ + } while (0) + +static int find_lower_writable(struct au_mvd_args *a) +{ + struct super_block *sb; + aufs_bindex_t bindex, bbot; + struct au_branch *br; + + sb = a->sb; + bindex = a->mvd_bsrc; + bbot = au_sbbot(sb); + if (a->mvdown.flags & AUFS_MVDOWN_FHSM_LOWER) + for (bindex++; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + if (au_br_fhsm(br->br_perm) + && !sb_rdonly(au_br_sb(br))) + return bindex; + } + else if (!(a->mvdown.flags & AUFS_MVDOWN_ROLOWER)) + for (bindex++; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + if (!au_br_rdonly(br)) + return bindex; + } + else + for (bindex++; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + if (!sb_rdonly(au_br_sb(br))) { + if (au_br_rdonly(br)) + a->mvdown.flags + |= AUFS_MVDOWN_ROLOWER_R; + return bindex; + } + } + + return -1; +} + +/* make the parent dir on bdst */ +static int au_do_mkdir(const unsigned char dmsg, struct au_mvd_args *a) +{ + int err; + + err = 0; + a->mvd_hdir_src = au_hi(a->dir, a->mvd_bsrc); + a->mvd_hdir_dst = au_hi(a->dir, a->mvd_bdst); + a->mvd_h_src_parent = au_h_dptr(a->parent, a->mvd_bsrc); + a->mvd_h_dst_parent = NULL; + if (au_dbbot(a->parent) >= a->mvd_bdst) + a->mvd_h_dst_parent = au_h_dptr(a->parent, a->mvd_bdst); + if (!a->mvd_h_dst_parent) { + err = au_cpdown_dirs(a->dentry, a->mvd_bdst); + if (unlikely(err)) { + AU_MVD_PR(dmsg, "cpdown_dirs failed\n"); + goto out; + } + a->mvd_h_dst_parent = au_h_dptr(a->parent, a->mvd_bdst); + } + +out: + AuTraceErr(err); + return err; +} + +/* lock them all */ +static int au_do_lock(const unsigned char dmsg, struct au_mvd_args *a) +{ + int err; + struct dentry *h_trap; + + a->mvd_h_src_sb = au_sbr_sb(a->sb, a->mvd_bsrc); + a->mvd_h_dst_sb = au_sbr_sb(a->sb, a->mvd_bdst); + err = au_pin(&a->mvd_pin_dst, a->dentry, a->mvd_bdst, + au_opt_udba(a->sb), + AuPin_MNT_WRITE | AuPin_DI_LOCKED); + AuTraceErr(err); + if (unlikely(err)) { + AU_MVD_PR(dmsg, "pin_dst failed\n"); + goto out; + } + + if (a->mvd_h_src_sb != a->mvd_h_dst_sb) { + a->rename_lock = 0; + au_pin_init(&a->mvd_pin_src, a->dentry, a->mvd_bsrc, + AuLsc_DI_PARENT, AuLsc_I_PARENT3, + au_opt_udba(a->sb), + AuPin_MNT_WRITE | AuPin_DI_LOCKED); + err = au_do_pin(&a->mvd_pin_src); + AuTraceErr(err); + a->mvd_h_src_dir = d_inode(a->mvd_h_src_parent); + if (unlikely(err)) { + AU_MVD_PR(dmsg, "pin_src failed\n"); + goto out_dst; + } + goto out; /* success */ + } + + a->rename_lock = 1; + au_pin_hdir_unlock(&a->mvd_pin_dst); + err = au_pin(&a->mvd_pin_src, a->dentry, a->mvd_bsrc, + au_opt_udba(a->sb), + AuPin_MNT_WRITE | AuPin_DI_LOCKED); + AuTraceErr(err); + a->mvd_h_src_dir = d_inode(a->mvd_h_src_parent); + if (unlikely(err)) { + AU_MVD_PR(dmsg, "pin_src failed\n"); + au_pin_hdir_lock(&a->mvd_pin_dst); + goto out_dst; + } + au_pin_hdir_unlock(&a->mvd_pin_src); + h_trap = vfsub_lock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, + a->mvd_h_dst_parent, a->mvd_hdir_dst); + if (h_trap) { + err = (h_trap != a->mvd_h_src_parent); + if (err) + err = (h_trap != a->mvd_h_dst_parent); + } + BUG_ON(err); /* it should never happen */ + if (unlikely(a->mvd_h_src_dir != au_pinned_h_dir(&a->mvd_pin_src))) { + err = -EBUSY; + AuTraceErr(err); + vfsub_unlock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, + a->mvd_h_dst_parent, a->mvd_hdir_dst); + au_pin_hdir_lock(&a->mvd_pin_src); + au_unpin(&a->mvd_pin_src); + au_pin_hdir_lock(&a->mvd_pin_dst); + goto out_dst; + } + goto out; /* success */ + +out_dst: + au_unpin(&a->mvd_pin_dst); +out: + AuTraceErr(err); + return err; +} + +static void au_do_unlock(const unsigned char dmsg, struct au_mvd_args *a) +{ + if (!a->rename_lock) + au_unpin(&a->mvd_pin_src); + else { + vfsub_unlock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, + a->mvd_h_dst_parent, a->mvd_hdir_dst); + au_pin_hdir_lock(&a->mvd_pin_src); + au_unpin(&a->mvd_pin_src); + au_pin_hdir_lock(&a->mvd_pin_dst); + } + au_unpin(&a->mvd_pin_dst); +} + +/* copy-down the file */ +static int au_do_cpdown(const unsigned char dmsg, struct au_mvd_args *a) +{ + int err; + struct au_cp_generic cpg = { + .dentry = a->dentry, + .bdst = a->mvd_bdst, + .bsrc = a->mvd_bsrc, + .len = -1, + .pin = &a->mvd_pin_dst, + .flags = AuCpup_DTIME | AuCpup_HOPEN + }; + + AuDbg("b%d, b%d\n", cpg.bsrc, cpg.bdst); + if (a->mvdown.flags & AUFS_MVDOWN_OWLOWER) + au_fset_cpup(cpg.flags, OVERWRITE); + if (a->mvdown.flags & AUFS_MVDOWN_ROLOWER) + au_fset_cpup(cpg.flags, RWDST); + err = au_sio_cpdown_simple(&cpg); + if (unlikely(err)) + AU_MVD_PR(dmsg, "cpdown failed\n"); + + AuTraceErr(err); + return err; +} + +/* + * unlink the whiteout on bdst if exist which may be created by UDBA while we + * were sleeping + */ +static int au_do_unlink_wh(const unsigned char dmsg, struct au_mvd_args *a) +{ + int err; + struct path h_path; + struct au_branch *br; + struct inode *delegated; + + br = au_sbr(a->sb, a->mvd_bdst); + h_path.dentry = au_wh_lkup(a->mvd_h_dst_parent, &a->dentry->d_name, br); + err = PTR_ERR(h_path.dentry); + if (IS_ERR(h_path.dentry)) { + AU_MVD_PR(dmsg, "wh_lkup failed\n"); + goto out; + } + + err = 0; + if (d_is_positive(h_path.dentry)) { + h_path.mnt = au_br_mnt(br); + delegated = NULL; + err = vfsub_unlink(d_inode(a->mvd_h_dst_parent), &h_path, + &delegated, /*force*/0); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal unlink\n"); + iput(delegated); + } + if (unlikely(err)) + AU_MVD_PR(dmsg, "wh_unlink failed\n"); + } + dput(h_path.dentry); + +out: + AuTraceErr(err); + return err; +} + +/* + * unlink the topmost h_dentry + */ +static int au_do_unlink(const unsigned char dmsg, struct au_mvd_args *a) +{ + int err; + struct path h_path; + struct inode *delegated; + + h_path.mnt = au_sbr_mnt(a->sb, a->mvd_bsrc); + h_path.dentry = au_h_dptr(a->dentry, a->mvd_bsrc); + delegated = NULL; + err = vfsub_unlink(a->mvd_h_src_dir, &h_path, &delegated, /*force*/0); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal unlink\n"); + iput(delegated); + } + if (unlikely(err)) + AU_MVD_PR(dmsg, "unlink failed\n"); + + AuTraceErr(err); + return err; +} + +/* Since mvdown succeeded, we ignore an error of this function */ +static void au_do_stfs(const unsigned char dmsg, struct au_mvd_args *a) +{ + int err; + struct au_branch *br; + + a->mvdown.flags |= AUFS_MVDOWN_STFS_FAILED; + br = au_sbr(a->sb, a->mvd_bsrc); + err = au_br_stfs(br, &a->mvdown.stbr[AUFS_MVDOWN_UPPER].stfs); + if (!err) { + br = au_sbr(a->sb, a->mvd_bdst); + a->mvdown.stbr[AUFS_MVDOWN_LOWER].brid = br->br_id; + err = au_br_stfs(br, &a->mvdown.stbr[AUFS_MVDOWN_LOWER].stfs); + } + if (!err) + a->mvdown.flags &= ~AUFS_MVDOWN_STFS_FAILED; + else + AU_MVD_PR(dmsg, "statfs failed (%d), ignored\n", err); +} + +/* + * copy-down the file and unlink the bsrc file. + * - unlink the bdst whout if exist + * - copy-down the file (with whtmp name and rename) + * - unlink the bsrc file + */ +static int au_do_mvdown(const unsigned char dmsg, struct au_mvd_args *a) +{ + int err; + + err = au_do_mkdir(dmsg, a); + if (!err) + err = au_do_lock(dmsg, a); + if (unlikely(err)) + goto out; + + /* + * do not revert the activities we made on bdst since they should be + * harmless in aufs. + */ + + err = au_do_cpdown(dmsg, a); + if (!err) + err = au_do_unlink_wh(dmsg, a); + if (!err && !(a->mvdown.flags & AUFS_MVDOWN_KUPPER)) + err = au_do_unlink(dmsg, a); + if (unlikely(err)) + goto out_unlock; + + AuDbg("%pd2, 0x%x, %d --> %d\n", + a->dentry, a->mvdown.flags, a->mvd_bsrc, a->mvd_bdst); + if (find_lower_writable(a) < 0) + a->mvdown.flags |= AUFS_MVDOWN_BOTTOM; + + if (a->mvdown.flags & AUFS_MVDOWN_STFS) + au_do_stfs(dmsg, a); + + /* maintain internal array */ + if (!(a->mvdown.flags & AUFS_MVDOWN_KUPPER)) { + au_set_h_dptr(a->dentry, a->mvd_bsrc, NULL); + au_set_dbtop(a->dentry, a->mvd_bdst); + au_set_h_iptr(a->inode, a->mvd_bsrc, NULL, /*flags*/0); + au_set_ibtop(a->inode, a->mvd_bdst); + } else { + /* hide the lower */ + au_set_h_dptr(a->dentry, a->mvd_bdst, NULL); + au_set_dbbot(a->dentry, a->mvd_bsrc); + au_set_h_iptr(a->inode, a->mvd_bdst, NULL, /*flags*/0); + au_set_ibbot(a->inode, a->mvd_bsrc); + } + if (au_dbbot(a->dentry) < a->mvd_bdst) + au_set_dbbot(a->dentry, a->mvd_bdst); + if (au_ibbot(a->inode) < a->mvd_bdst) + au_set_ibbot(a->inode, a->mvd_bdst); + +out_unlock: + au_do_unlock(dmsg, a); +out: + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* make sure the file is idle */ +static int au_mvd_args_busy(const unsigned char dmsg, struct au_mvd_args *a) +{ + int err, plinked; + + err = 0; + plinked = !!au_opt_test(au_mntflags(a->sb), PLINK); + if (au_dbtop(a->dentry) == a->mvd_bsrc + && au_dcount(a->dentry) == 1 + && atomic_read(&a->inode->i_count) == 1 + /* && a->mvd_h_src_inode->i_nlink == 1 */ + && (!plinked || !au_plink_test(a->inode)) + && a->inode->i_nlink == 1) + goto out; + + err = -EBUSY; + AU_MVD_PR(dmsg, + "b%d, d{b%d, c%d?}, i{c%d?, l%u}, hi{l%u}, p{%d, %d}\n", + a->mvd_bsrc, au_dbtop(a->dentry), au_dcount(a->dentry), + atomic_read(&a->inode->i_count), a->inode->i_nlink, + a->mvd_h_src_inode->i_nlink, + plinked, plinked ? au_plink_test(a->inode) : 0); + +out: + AuTraceErr(err); + return err; +} + +/* make sure the parent dir is fine */ +static int au_mvd_args_parent(const unsigned char dmsg, + struct au_mvd_args *a) +{ + int err; + aufs_bindex_t bindex; + + err = 0; + if (unlikely(au_alive_dir(a->parent))) { + err = -ENOENT; + AU_MVD_PR(dmsg, "parent dir is dead\n"); + goto out; + } + + a->bopq = au_dbdiropq(a->parent); + bindex = au_wbr_nonopq(a->dentry, a->mvd_bdst); + AuDbg("b%d\n", bindex); + if (unlikely((bindex >= 0 && bindex < a->mvd_bdst) + || (a->bopq != -1 && a->bopq < a->mvd_bdst))) { + err = -EINVAL; + a->mvd_errno = EAU_MVDOWN_OPAQUE; + AU_MVD_PR(dmsg, "ancestor is opaque b%d, b%d\n", + a->bopq, a->mvd_bdst); + } + +out: + AuTraceErr(err); + return err; +} + +static int au_mvd_args_intermediate(const unsigned char dmsg, + struct au_mvd_args *a) +{ + int err; + struct au_dinfo *dinfo, *tmp; + + /* lookup the next lower positive entry */ + err = -ENOMEM; + tmp = au_di_alloc(a->sb, AuLsc_DI_TMP); + if (unlikely(!tmp)) + goto out; + + a->bfound = -1; + a->bwh = -1; + dinfo = au_di(a->dentry); + au_di_cp(tmp, dinfo); + au_di_swap(tmp, dinfo); + + /* returns the number of positive dentries */ + err = au_lkup_dentry(a->dentry, a->mvd_bsrc + 1, + /* AuLkup_IGNORE_PERM */ 0); + if (!err) + a->bwh = au_dbwh(a->dentry); + else if (err > 0) + a->bfound = au_dbtop(a->dentry); + + au_di_swap(tmp, dinfo); + au_rw_write_unlock(&tmp->di_rwsem); + au_di_free(tmp); + if (unlikely(err < 0)) + AU_MVD_PR(dmsg, "failed look-up lower\n"); + + /* + * here, we have these cases. + * bfound == -1 + * no positive dentry under bsrc. there are more sub-cases. + * bwh < 0 + * there no whiteout, we can safely move-down. + * bwh <= bsrc + * impossible + * bsrc < bwh && bwh < bdst + * there is a whiteout on RO branch. cannot proceed. + * bwh == bdst + * there is a whiteout on the RW target branch. it should + * be removed. + * bdst < bwh + * there is a whiteout somewhere unrelated branch. + * -1 < bfound && bfound <= bsrc + * impossible. + * bfound < bdst + * found, but it is on RO branch between bsrc and bdst. cannot + * proceed. + * bfound == bdst + * found, replace it if AUFS_MVDOWN_FORCE is set. otherwise return + * error. + * bdst < bfound + * found, after we create the file on bdst, it will be hidden. + */ + + AuDebugOn(a->bfound == -1 + && a->bwh != -1 + && a->bwh <= a->mvd_bsrc); + AuDebugOn(-1 < a->bfound + && a->bfound <= a->mvd_bsrc); + + err = -EINVAL; + if (a->bfound == -1 + && a->mvd_bsrc < a->bwh + && a->bwh != -1 + && a->bwh < a->mvd_bdst) { + a->mvd_errno = EAU_MVDOWN_WHITEOUT; + AU_MVD_PR(dmsg, "bsrc %d, bdst %d, bfound %d, bwh %d\n", + a->mvd_bsrc, a->mvd_bdst, a->bfound, a->bwh); + goto out; + } else if (a->bfound != -1 && a->bfound < a->mvd_bdst) { + a->mvd_errno = EAU_MVDOWN_UPPER; + AU_MVD_PR(dmsg, "bdst %d, bfound %d\n", + a->mvd_bdst, a->bfound); + goto out; + } + + err = 0; /* success */ + +out: + AuTraceErr(err); + return err; +} + +static int au_mvd_args_exist(const unsigned char dmsg, struct au_mvd_args *a) +{ + int err; + + err = 0; + if (!(a->mvdown.flags & AUFS_MVDOWN_OWLOWER) + && a->bfound == a->mvd_bdst) + err = -EEXIST; + AuTraceErr(err); + return err; +} + +static int au_mvd_args(const unsigned char dmsg, struct au_mvd_args *a) +{ + int err; + struct au_branch *br; + + err = -EISDIR; + if (unlikely(S_ISDIR(a->inode->i_mode))) + goto out; + + err = -EINVAL; + if (!(a->mvdown.flags & AUFS_MVDOWN_BRID_UPPER)) + a->mvd_bsrc = au_ibtop(a->inode); + else { + a->mvd_bsrc = au_br_index(a->sb, a->mvd_src_brid); + if (unlikely(a->mvd_bsrc < 0 + || (a->mvd_bsrc < au_dbtop(a->dentry) + || au_dbbot(a->dentry) < a->mvd_bsrc + || !au_h_dptr(a->dentry, a->mvd_bsrc)) + || (a->mvd_bsrc < au_ibtop(a->inode) + || au_ibbot(a->inode) < a->mvd_bsrc + || !au_h_iptr(a->inode, a->mvd_bsrc)))) { + a->mvd_errno = EAU_MVDOWN_NOUPPER; + AU_MVD_PR(dmsg, "no upper\n"); + goto out; + } + } + if (unlikely(a->mvd_bsrc == au_sbbot(a->sb))) { + a->mvd_errno = EAU_MVDOWN_BOTTOM; + AU_MVD_PR(dmsg, "on the bottom\n"); + goto out; + } + a->mvd_h_src_inode = au_h_iptr(a->inode, a->mvd_bsrc); + br = au_sbr(a->sb, a->mvd_bsrc); + err = au_br_rdonly(br); + if (!(a->mvdown.flags & AUFS_MVDOWN_ROUPPER)) { + if (unlikely(err)) + goto out; + } else if (!(vfsub_native_ro(a->mvd_h_src_inode) + || IS_APPEND(a->mvd_h_src_inode))) { + if (err) + a->mvdown.flags |= AUFS_MVDOWN_ROUPPER_R; + /* go on */ + } else + goto out; + + err = -EINVAL; + if (!(a->mvdown.flags & AUFS_MVDOWN_BRID_LOWER)) { + a->mvd_bdst = find_lower_writable(a); + if (unlikely(a->mvd_bdst < 0)) { + a->mvd_errno = EAU_MVDOWN_BOTTOM; + AU_MVD_PR(dmsg, "no writable lower branch\n"); + goto out; + } + } else { + a->mvd_bdst = au_br_index(a->sb, a->mvd_dst_brid); + if (unlikely(a->mvd_bdst < 0 + || au_sbbot(a->sb) < a->mvd_bdst)) { + a->mvd_errno = EAU_MVDOWN_NOLOWERBR; + AU_MVD_PR(dmsg, "no lower brid\n"); + goto out; + } + } + + err = au_mvd_args_busy(dmsg, a); + if (!err) + err = au_mvd_args_parent(dmsg, a); + if (!err) + err = au_mvd_args_intermediate(dmsg, a); + if (!err) + err = au_mvd_args_exist(dmsg, a); + if (!err) + AuDbg("b%d, b%d\n", a->mvd_bsrc, a->mvd_bdst); + +out: + AuTraceErr(err); + return err; +} + +int au_mvdown(struct dentry *dentry, struct aufs_mvdown __user *uarg) +{ + int err, e; + unsigned char dmsg; + struct au_mvd_args *args; + struct inode *inode; + + inode = d_inode(dentry); + err = -EPERM; + if (unlikely(!capable(CAP_SYS_ADMIN))) + goto out; + + err = -ENOMEM; + args = kmalloc(sizeof(*args), GFP_NOFS); + if (unlikely(!args)) + goto out; + + err = copy_from_user(&args->mvdown, uarg, sizeof(args->mvdown)); + if (!err) + /* VERIFY_WRITE */ + err = !access_ok(uarg, sizeof(*uarg)); + if (unlikely(err)) { + err = -EFAULT; + AuTraceErr(err); + goto out_free; + } + AuDbg("flags 0x%x\n", args->mvdown.flags); + args->mvdown.flags &= ~(AUFS_MVDOWN_ROLOWER_R | AUFS_MVDOWN_ROUPPER_R); + args->mvdown.au_errno = 0; + args->dentry = dentry; + args->inode = inode; + args->sb = dentry->d_sb; + + err = -ENOENT; + dmsg = !!(args->mvdown.flags & AUFS_MVDOWN_DMSG); + args->parent = dget_parent(dentry); + args->dir = d_inode(args->parent); + inode_lock_nested(args->dir, I_MUTEX_PARENT); + dput(args->parent); + if (unlikely(args->parent != dentry->d_parent)) { + AU_MVD_PR(dmsg, "parent dir is moved\n"); + goto out_dir; + } + + inode_lock_nested(inode, I_MUTEX_CHILD); + err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_NOPLMW); + if (unlikely(err)) + goto out_inode; + + di_write_lock_parent(args->parent); + err = au_mvd_args(dmsg, args); + if (unlikely(err)) + goto out_parent; + + err = au_do_mvdown(dmsg, args); + if (unlikely(err)) + goto out_parent; + + au_cpup_attr_timesizes(args->dir); + au_cpup_attr_timesizes(inode); + if (!(args->mvdown.flags & AUFS_MVDOWN_KUPPER)) + au_cpup_igen(inode, au_h_iptr(inode, args->mvd_bdst)); + /* au_digen_dec(dentry); */ + +out_parent: + di_write_unlock(args->parent); + aufs_read_unlock(dentry, AuLock_DW); +out_inode: + inode_unlock(inode); +out_dir: + inode_unlock(args->dir); +out_free: + e = copy_to_user(uarg, &args->mvdown, sizeof(args->mvdown)); + if (unlikely(e)) + err = -EFAULT; + au_kfree_rcu(args); +out: + AuTraceErr(err); + return err; +} diff --git a/fs/aufs/opts.c b/fs/aufs/opts.c new file mode 100644 index 0000000000000..703ff034c4672 --- /dev/null +++ b/fs/aufs/opts.c @@ -0,0 +1,1032 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * mount options/flags + */ + +#include /* a distribution requires */ +#include +#include "aufs.h" + +/* ---------------------------------------------------------------------- */ + +static const char *au_parser_pattern(int val, match_table_t tbl) +{ + struct match_token *p; + + p = tbl; + while (p->pattern) { + if (p->token == val) + return p->pattern; + p++; + } + BUG(); + return "??"; +} + +static const char *au_optstr(int *val, match_table_t tbl) +{ + struct match_token *p; + int v; + + v = *val; + if (!v) + goto out; + p = tbl; + while (p->pattern) { + if (p->token + && (v & p->token) == p->token) { + *val &= ~p->token; + return p->pattern; + } + p++; + } + +out: + return NULL; +} + +/* ---------------------------------------------------------------------- */ + +static match_table_t brperm = { + {AuBrPerm_RO, AUFS_BRPERM_RO}, + {AuBrPerm_RR, AUFS_BRPERM_RR}, + {AuBrPerm_RW, AUFS_BRPERM_RW}, + {0, NULL} +}; + +static match_table_t brattr = { + /* general */ + {AuBrAttr_COO_REG, AUFS_BRATTR_COO_REG}, + {AuBrAttr_COO_ALL, AUFS_BRATTR_COO_ALL}, + /* 'unpin' attrib is meaningless since linux-3.18-rc1 */ + {AuBrAttr_UNPIN, AUFS_BRATTR_UNPIN}, +#ifdef CONFIG_AUFS_FHSM + {AuBrAttr_FHSM, AUFS_BRATTR_FHSM}, +#endif +#ifdef CONFIG_AUFS_XATTR + {AuBrAttr_ICEX, AUFS_BRATTR_ICEX}, + {AuBrAttr_ICEX_SEC, AUFS_BRATTR_ICEX_SEC}, + {AuBrAttr_ICEX_SYS, AUFS_BRATTR_ICEX_SYS}, + {AuBrAttr_ICEX_TR, AUFS_BRATTR_ICEX_TR}, + {AuBrAttr_ICEX_USR, AUFS_BRATTR_ICEX_USR}, + {AuBrAttr_ICEX_OTH, AUFS_BRATTR_ICEX_OTH}, +#endif + + /* ro/rr branch */ + {AuBrRAttr_WH, AUFS_BRRATTR_WH}, + + /* rw branch */ + {AuBrWAttr_MOO, AUFS_BRWATTR_MOO}, + {AuBrWAttr_NoLinkWH, AUFS_BRWATTR_NLWH}, + + {0, NULL} +}; + +static int br_attr_val(char *str, match_table_t table, substring_t args[]) +{ + int attr, v; + char *p; + + attr = 0; + do { + p = strchr(str, '+'); + if (p) + *p = 0; + v = match_token(str, table, args); + if (v) { + if (v & AuBrAttr_CMOO_Mask) + attr &= ~AuBrAttr_CMOO_Mask; + attr |= v; + } else { + if (p) + *p = '+'; + pr_warn("ignored branch attribute %s\n", str); + break; + } + if (p) + str = p + 1; + } while (p); + + return attr; +} + +static int au_do_optstr_br_attr(au_br_perm_str_t *str, int perm) +{ + int sz; + const char *p; + char *q; + + q = str->a; + *q = 0; + p = au_optstr(&perm, brattr); + if (p) { + sz = strlen(p); + memcpy(q, p, sz + 1); + q += sz; + } else + goto out; + + do { + p = au_optstr(&perm, brattr); + if (p) { + *q++ = '+'; + sz = strlen(p); + memcpy(q, p, sz + 1); + q += sz; + } + } while (p); + +out: + return q - str->a; +} + +int au_br_perm_val(char *perm) +{ + int val, bad, sz; + char *p; + substring_t args[MAX_OPT_ARGS]; + au_br_perm_str_t attr; + + p = strchr(perm, '+'); + if (p) + *p = 0; + val = match_token(perm, brperm, args); + if (!val) { + if (p) + *p = '+'; + pr_warn("ignored branch permission %s\n", perm); + val = AuBrPerm_RO; + goto out; + } + if (!p) + goto out; + + val |= br_attr_val(p + 1, brattr, args); + + bad = 0; + switch (val & AuBrPerm_Mask) { + case AuBrPerm_RO: + case AuBrPerm_RR: + bad = val & AuBrWAttr_Mask; + val &= ~AuBrWAttr_Mask; + break; + case AuBrPerm_RW: + bad = val & AuBrRAttr_Mask; + val &= ~AuBrRAttr_Mask; + break; + } + + /* + * 'unpin' attrib becomes meaningless since linux-3.18-rc1, but aufs + * does not treat it as an error, just warning. + * this is a tiny guard for the user operation. + */ + if (val & AuBrAttr_UNPIN) { + bad |= AuBrAttr_UNPIN; + val &= ~AuBrAttr_UNPIN; + } + + if (unlikely(bad)) { + sz = au_do_optstr_br_attr(&attr, bad); + AuDebugOn(!sz); + pr_warn("ignored branch attribute %s\n", attr.a); + } + +out: + return val; +} + +void au_optstr_br_perm(au_br_perm_str_t *str, int perm) +{ + au_br_perm_str_t attr; + const char *p; + char *q; + int sz; + + q = str->a; + p = au_optstr(&perm, brperm); + AuDebugOn(!p || !*p); + sz = strlen(p); + memcpy(q, p, sz + 1); + q += sz; + + sz = au_do_optstr_br_attr(&attr, perm); + if (sz) { + *q++ = '+'; + memcpy(q, attr.a, sz + 1); + } + + AuDebugOn(strlen(str->a) >= sizeof(str->a)); +} + +/* ---------------------------------------------------------------------- */ + +static match_table_t udbalevel = { + {AuOpt_UDBA_REVAL, "reval"}, + {AuOpt_UDBA_NONE, "none"}, +#ifdef CONFIG_AUFS_HNOTIFY + {AuOpt_UDBA_HNOTIFY, "notify"}, /* abstraction */ +#ifdef CONFIG_AUFS_HFSNOTIFY + {AuOpt_UDBA_HNOTIFY, "fsnotify"}, +#endif +#endif + {-1, NULL} +}; + +int au_udba_val(char *str) +{ + substring_t args[MAX_OPT_ARGS]; + + return match_token(str, udbalevel, args); +} + +const char *au_optstr_udba(int udba) +{ + return au_parser_pattern(udba, udbalevel); +} + +/* ---------------------------------------------------------------------- */ + +static match_table_t au_wbr_create_policy = { + {AuWbrCreate_TDP, "tdp"}, + {AuWbrCreate_TDP, "top-down-parent"}, + {AuWbrCreate_RR, "rr"}, + {AuWbrCreate_RR, "round-robin"}, + {AuWbrCreate_MFS, "mfs"}, + {AuWbrCreate_MFS, "most-free-space"}, + {AuWbrCreate_MFSV, "mfs:%d"}, + {AuWbrCreate_MFSV, "most-free-space:%d"}, + + /* top-down regardless the parent, and then mfs */ + {AuWbrCreate_TDMFS, "tdmfs:%d"}, + {AuWbrCreate_TDMFSV, "tdmfs:%d:%d"}, + + {AuWbrCreate_MFSRR, "mfsrr:%d"}, + {AuWbrCreate_MFSRRV, "mfsrr:%d:%d"}, + {AuWbrCreate_PMFS, "pmfs"}, + {AuWbrCreate_PMFSV, "pmfs:%d"}, + {AuWbrCreate_PMFSRR, "pmfsrr:%d"}, + {AuWbrCreate_PMFSRRV, "pmfsrr:%d:%d"}, + + {-1, NULL} +}; + +static int au_wbr_mfs_wmark(substring_t *arg, char *str, + struct au_opt_wbr_create *create) +{ + int err; + unsigned long long ull; + + err = 0; + if (!match_u64(arg, &ull)) + create->mfsrr_watermark = ull; + else { + pr_err("bad integer in %s\n", str); + err = -EINVAL; + } + + return err; +} + +static int au_wbr_mfs_sec(substring_t *arg, char *str, + struct au_opt_wbr_create *create) +{ + int n, err; + + err = 0; + if (!match_int(arg, &n) && 0 <= n && n <= AUFS_MFS_MAX_SEC) + create->mfs_second = n; + else { + pr_err("bad integer in %s\n", str); + err = -EINVAL; + } + + return err; +} + +int au_wbr_create_val(char *str, struct au_opt_wbr_create *create) +{ + int err, e; + substring_t args[MAX_OPT_ARGS]; + + err = match_token(str, au_wbr_create_policy, args); + create->wbr_create = err; + switch (err) { + case AuWbrCreate_MFSRRV: + case AuWbrCreate_TDMFSV: + case AuWbrCreate_PMFSRRV: + e = au_wbr_mfs_wmark(&args[0], str, create); + if (!e) + e = au_wbr_mfs_sec(&args[1], str, create); + if (unlikely(e)) + err = e; + break; + case AuWbrCreate_MFSRR: + case AuWbrCreate_TDMFS: + case AuWbrCreate_PMFSRR: + e = au_wbr_mfs_wmark(&args[0], str, create); + if (unlikely(e)) { + err = e; + break; + } + fallthrough; + case AuWbrCreate_MFS: + case AuWbrCreate_PMFS: + create->mfs_second = AUFS_MFS_DEF_SEC; + break; + case AuWbrCreate_MFSV: + case AuWbrCreate_PMFSV: + e = au_wbr_mfs_sec(&args[0], str, create); + if (unlikely(e)) + err = e; + break; + } + + return err; +} + +const char *au_optstr_wbr_create(int wbr_create) +{ + return au_parser_pattern(wbr_create, au_wbr_create_policy); +} + +static match_table_t au_wbr_copyup_policy = { + {AuWbrCopyup_TDP, "tdp"}, + {AuWbrCopyup_TDP, "top-down-parent"}, + {AuWbrCopyup_BUP, "bup"}, + {AuWbrCopyup_BUP, "bottom-up-parent"}, + {AuWbrCopyup_BU, "bu"}, + {AuWbrCopyup_BU, "bottom-up"}, + {-1, NULL} +}; + +int au_wbr_copyup_val(char *str) +{ + substring_t args[MAX_OPT_ARGS]; + + return match_token(str, au_wbr_copyup_policy, args); +} + +const char *au_optstr_wbr_copyup(int wbr_copyup) +{ + return au_parser_pattern(wbr_copyup, au_wbr_copyup_policy); +} + +/* ---------------------------------------------------------------------- */ + +int au_opt_add(struct au_opt *opt, char *opt_str, unsigned long sb_flags, + aufs_bindex_t bindex) +{ + int err; + struct au_opt_add *add = &opt->add; + char *p; + + add->bindex = bindex; + add->perm = AuBrPerm_RO; + add->pathname = opt_str; + p = strchr(opt_str, '='); + if (p) { + *p++ = 0; + if (*p) + add->perm = au_br_perm_val(p); + } + + err = vfsub_kern_path(add->pathname, AuOpt_LkupDirFlags, &add->path); + if (!err) { + if (!p) { + add->perm = AuBrPerm_RO; + if (au_test_fs_rr(add->path.dentry->d_sb)) + add->perm = AuBrPerm_RR; + else if (!bindex && !(sb_flags & SB_RDONLY)) + add->perm = AuBrPerm_RW; + } + opt->type = Opt_add; + goto out; + } + pr_err("lookup failed %s (%d)\n", add->pathname, err); + err = -EINVAL; + +out: + return err; +} + +static int au_opt_wbr_create(struct super_block *sb, + struct au_opt_wbr_create *create) +{ + int err; + struct au_sbinfo *sbinfo; + + SiMustWriteLock(sb); + + err = 1; /* handled */ + sbinfo = au_sbi(sb); + if (sbinfo->si_wbr_create_ops->fin) { + err = sbinfo->si_wbr_create_ops->fin(sb); + if (!err) + err = 1; + } + + sbinfo->si_wbr_create = create->wbr_create; + sbinfo->si_wbr_create_ops = au_wbr_create_ops + create->wbr_create; + switch (create->wbr_create) { + case AuWbrCreate_MFSRRV: + case AuWbrCreate_MFSRR: + case AuWbrCreate_TDMFS: + case AuWbrCreate_TDMFSV: + case AuWbrCreate_PMFSRR: + case AuWbrCreate_PMFSRRV: + sbinfo->si_wbr_mfs.mfsrr_watermark = create->mfsrr_watermark; + fallthrough; + case AuWbrCreate_MFS: + case AuWbrCreate_MFSV: + case AuWbrCreate_PMFS: + case AuWbrCreate_PMFSV: + sbinfo->si_wbr_mfs.mfs_expire + = msecs_to_jiffies(create->mfs_second * MSEC_PER_SEC); + break; + } + + if (sbinfo->si_wbr_create_ops->init) + sbinfo->si_wbr_create_ops->init(sb); /* ignore */ + + return err; +} + +/* + * returns, + * plus: processed without an error + * zero: unprocessed + */ +static int au_opt_simple(struct super_block *sb, struct au_opt *opt, + struct au_opts *opts) +{ + int err; + struct au_sbinfo *sbinfo; + + SiMustWriteLock(sb); + + err = 1; /* handled */ + sbinfo = au_sbi(sb); + switch (opt->type) { + case Opt_udba: + sbinfo->si_mntflags &= ~AuOptMask_UDBA; + sbinfo->si_mntflags |= opt->udba; + opts->given_udba |= opt->udba; + break; + + case Opt_plink: + if (opt->tf) + au_opt_set(sbinfo->si_mntflags, PLINK); + else { + if (au_opt_test(sbinfo->si_mntflags, PLINK)) + au_plink_put(sb, /*verbose*/1); + au_opt_clr(sbinfo->si_mntflags, PLINK); + } + break; + case Opt_list_plink: + if (au_opt_test(sbinfo->si_mntflags, PLINK)) + au_plink_list(sb); + break; + + case Opt_dio: + if (opt->tf) { + au_opt_set(sbinfo->si_mntflags, DIO); + au_fset_opts(opts->flags, REFRESH_DYAOP); + } else { + au_opt_clr(sbinfo->si_mntflags, DIO); + au_fset_opts(opts->flags, REFRESH_DYAOP); + } + break; + + case Opt_fhsm_sec: + au_fhsm_set(sbinfo, opt->fhsm_second); + break; + + case Opt_diropq_a: + au_opt_set(sbinfo->si_mntflags, ALWAYS_DIROPQ); + break; + case Opt_diropq_w: + au_opt_clr(sbinfo->si_mntflags, ALWAYS_DIROPQ); + break; + + case Opt_warn_perm: + if (opt->tf) + au_opt_set(sbinfo->si_mntflags, WARN_PERM); + else + au_opt_clr(sbinfo->si_mntflags, WARN_PERM); + break; + + case Opt_verbose: + if (opt->tf) + au_opt_set(sbinfo->si_mntflags, VERBOSE); + else + au_opt_clr(sbinfo->si_mntflags, VERBOSE); + break; + + case Opt_sum: + if (opt->tf) + au_opt_set(sbinfo->si_mntflags, SUM); + else { + au_opt_clr(sbinfo->si_mntflags, SUM); + au_opt_clr(sbinfo->si_mntflags, SUM_W); + } + break; + case Opt_wsum: + au_opt_clr(sbinfo->si_mntflags, SUM); + au_opt_set(sbinfo->si_mntflags, SUM_W); + break; + + case Opt_wbr_create: + err = au_opt_wbr_create(sb, &opt->wbr_create); + break; + case Opt_wbr_copyup: + sbinfo->si_wbr_copyup = opt->wbr_copyup; + sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + opt->wbr_copyup; + break; + + case Opt_dirwh: + sbinfo->si_dirwh = opt->dirwh; + break; + + case Opt_rdcache: + sbinfo->si_rdcache + = msecs_to_jiffies(opt->rdcache * MSEC_PER_SEC); + break; + case Opt_rdblk: + sbinfo->si_rdblk = opt->rdblk; + break; + case Opt_rdhash: + sbinfo->si_rdhash = opt->rdhash; + break; + + case Opt_shwh: + if (opt->tf) + au_opt_set(sbinfo->si_mntflags, SHWH); + else + au_opt_clr(sbinfo->si_mntflags, SHWH); + break; + + case Opt_dirperm1: + if (opt->tf) + au_opt_set(sbinfo->si_mntflags, DIRPERM1); + else + au_opt_clr(sbinfo->si_mntflags, DIRPERM1); + break; + + case Opt_trunc_xino: + if (opt->tf) + au_opt_set(sbinfo->si_mntflags, TRUNC_XINO); + else + au_opt_clr(sbinfo->si_mntflags, TRUNC_XINO); + break; + + case Opt_trunc_xino_path: + case Opt_itrunc_xino: + err = au_xino_trunc(sb, opt->xino_itrunc.bindex, + /*idx_begin*/0); + if (!err) + err = 1; + break; + + case Opt_trunc_xib: + if (opt->tf) + au_fset_opts(opts->flags, TRUNC_XIB); + else + au_fclr_opts(opts->flags, TRUNC_XIB); + break; + + case Opt_dirren: + err = 1; + if (opt->tf) { + if (!au_opt_test(sbinfo->si_mntflags, DIRREN)) { + err = au_dr_opt_set(sb); + if (!err) + err = 1; + } + if (err == 1) + au_opt_set(sbinfo->si_mntflags, DIRREN); + } else { + if (au_opt_test(sbinfo->si_mntflags, DIRREN)) { + err = au_dr_opt_clr(sb, au_ftest_opts(opts->flags, + DR_FLUSHED)); + if (!err) + err = 1; + } + if (err == 1) + au_opt_clr(sbinfo->si_mntflags, DIRREN); + } + break; + + case Opt_acl: + if (opt->tf) + sb->s_flags |= SB_POSIXACL; + else + sb->s_flags &= ~SB_POSIXACL; + break; + + default: + err = 0; + break; + } + + return err; +} + +/* + * returns tri-state. + * plus: processed without an error + * zero: unprocessed + * minus: error + */ +static int au_opt_br(struct super_block *sb, struct au_opt *opt, + struct au_opts *opts) +{ + int err, do_refresh; + + err = 0; + switch (opt->type) { + case Opt_append: + opt->add.bindex = au_sbbot(sb) + 1; + if (opt->add.bindex < 0) + opt->add.bindex = 0; + goto add; + /* Always goto add, not fallthrough */ + case Opt_prepend: + opt->add.bindex = 0; + fallthrough; + add: /* indented label */ + case Opt_add: + err = au_br_add(sb, &opt->add, + au_ftest_opts(opts->flags, REMOUNT)); + if (!err) { + err = 1; + au_fset_opts(opts->flags, REFRESH); + } + break; + + case Opt_del: + case Opt_idel: + err = au_br_del(sb, &opt->del, + au_ftest_opts(opts->flags, REMOUNT)); + if (!err) { + err = 1; + au_fset_opts(opts->flags, TRUNC_XIB); + au_fset_opts(opts->flags, REFRESH); + } + break; + + case Opt_mod: + case Opt_imod: + err = au_br_mod(sb, &opt->mod, + au_ftest_opts(opts->flags, REMOUNT), + &do_refresh); + if (!err) { + err = 1; + if (do_refresh) + au_fset_opts(opts->flags, REFRESH); + } + break; + } + return err; +} + +static int au_opt_xino(struct super_block *sb, struct au_opt *opt, + struct au_opt_xino **opt_xino, + struct au_opts *opts) +{ + int err; + + err = 0; + switch (opt->type) { + case Opt_xino: + err = au_xino_set(sb, &opt->xino, + !!au_ftest_opts(opts->flags, REMOUNT)); + if (!err) + *opt_xino = &opt->xino; + break; + case Opt_noxino: + au_xino_clr(sb); + *opt_xino = (void *)-1; + break; + } + + return err; +} + +int au_opts_verify(struct super_block *sb, unsigned long sb_flags, + unsigned int pending) +{ + int err, fhsm; + aufs_bindex_t bindex, bbot; + unsigned char do_plink, skip, do_free, can_no_dreval; + struct au_branch *br; + struct au_wbr *wbr; + struct dentry *root, *dentry; + struct inode *dir, *h_dir; + struct au_sbinfo *sbinfo; + struct au_hinode *hdir; + + SiMustAnyLock(sb); + + sbinfo = au_sbi(sb); + AuDebugOn(!(sbinfo->si_mntflags & AuOptMask_UDBA)); + + if (!(sb_flags & SB_RDONLY)) { + if (unlikely(!au_br_writable(au_sbr_perm(sb, 0)))) + pr_warn("first branch should be rw\n"); + if (unlikely(au_opt_test(sbinfo->si_mntflags, SHWH))) + pr_warn_once("shwh should be used with ro\n"); + } + + if (au_opt_test((sbinfo->si_mntflags | pending), UDBA_HNOTIFY) + && !au_opt_test(sbinfo->si_mntflags, XINO)) + pr_warn_once("udba=*notify requires xino\n"); + + if (au_opt_test(sbinfo->si_mntflags, DIRPERM1)) + pr_warn_once("dirperm1 breaks the protection" + " by the permission bits on the lower branch\n"); + + err = 0; + fhsm = 0; + root = sb->s_root; + dir = d_inode(root); + do_plink = !!au_opt_test(sbinfo->si_mntflags, PLINK); + can_no_dreval = !!au_opt_test((sbinfo->si_mntflags | pending), + UDBA_NONE); + bbot = au_sbbot(sb); + for (bindex = 0; !err && bindex <= bbot; bindex++) { + skip = 0; + h_dir = au_h_iptr(dir, bindex); + br = au_sbr(sb, bindex); + + if ((br->br_perm & AuBrAttr_ICEX) + && !h_dir->i_op->listxattr) + br->br_perm &= ~AuBrAttr_ICEX; +#if 0 /* untested */ + if ((br->br_perm & AuBrAttr_ICEX_SEC) + && (au_br_sb(br)->s_flags & SB_NOSEC)) + br->br_perm &= ~AuBrAttr_ICEX_SEC; +#endif + + do_free = 0; + wbr = br->br_wbr; + if (wbr) + wbr_wh_read_lock(wbr); + + if (!au_br_writable(br->br_perm)) { + do_free = !!wbr; + skip = (!wbr + || (!wbr->wbr_whbase + && !wbr->wbr_plink + && !wbr->wbr_orph)); + } else if (!au_br_wh_linkable(br->br_perm)) { + /* skip = (!br->br_whbase && !br->br_orph); */ + skip = (!wbr || !wbr->wbr_whbase); + if (skip && wbr) { + if (do_plink) + skip = !!wbr->wbr_plink; + else + skip = !wbr->wbr_plink; + } + } else { + /* skip = (br->br_whbase && br->br_ohph); */ + skip = (wbr && wbr->wbr_whbase); + if (skip) { + if (do_plink) + skip = !!wbr->wbr_plink; + else + skip = !wbr->wbr_plink; + } + } + if (wbr) + wbr_wh_read_unlock(wbr); + + if (can_no_dreval) { + dentry = br->br_path.dentry; + spin_lock(&dentry->d_lock); + if (dentry->d_flags & + (DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE)) + can_no_dreval = 0; + spin_unlock(&dentry->d_lock); + } + + if (au_br_fhsm(br->br_perm)) { + fhsm++; + AuDebugOn(!br->br_fhsm); + } + + if (skip) + continue; + + hdir = au_hi(dir, bindex); + au_hn_inode_lock_nested(hdir, AuLsc_I_PARENT); + if (wbr) + wbr_wh_write_lock(wbr); + err = au_wh_init(br, sb); + if (wbr) + wbr_wh_write_unlock(wbr); + au_hn_inode_unlock(hdir); + + if (!err && do_free) { + au_kfree_rcu(wbr); + br->br_wbr = NULL; + } + } + + if (can_no_dreval) + au_fset_si(sbinfo, NO_DREVAL); + else + au_fclr_si(sbinfo, NO_DREVAL); + + if (fhsm >= 2) { + au_fset_si(sbinfo, FHSM); + for (bindex = bbot; bindex >= 0; bindex--) { + br = au_sbr(sb, bindex); + if (au_br_fhsm(br->br_perm)) { + au_fhsm_set_bottom(sb, bindex); + break; + } + } + } else { + au_fclr_si(sbinfo, FHSM); + au_fhsm_set_bottom(sb, -1); + } + + return err; +} + +int au_opts_mount(struct super_block *sb, struct au_opts *opts) +{ + int err; + unsigned int tmp; + aufs_bindex_t bindex, bbot; + struct au_opt *opt; + struct au_opt_xino *opt_xino, xino; + struct au_sbinfo *sbinfo; + struct au_branch *br; + struct inode *dir; + + SiMustWriteLock(sb); + + err = 0; + opt_xino = NULL; + opt = opts->opt; + while (err >= 0 && opt->type != Opt_tail) + err = au_opt_simple(sb, opt++, opts); + if (err > 0) + err = 0; + else if (unlikely(err < 0)) + goto out; + + /* disable xino and udba temporary */ + sbinfo = au_sbi(sb); + tmp = sbinfo->si_mntflags; + au_opt_clr(sbinfo->si_mntflags, XINO); + au_opt_set_udba(sbinfo->si_mntflags, UDBA_REVAL); + + opt = opts->opt; + while (err >= 0 && opt->type != Opt_tail) + err = au_opt_br(sb, opt++, opts); + if (err > 0) + err = 0; + else if (unlikely(err < 0)) + goto out; + + bbot = au_sbbot(sb); + if (unlikely(bbot < 0)) { + err = -EINVAL; + pr_err("no branches\n"); + goto out; + } + + if (au_opt_test(tmp, XINO)) + au_opt_set(sbinfo->si_mntflags, XINO); + opt = opts->opt; + while (!err && opt->type != Opt_tail) + err = au_opt_xino(sb, opt++, &opt_xino, opts); + if (unlikely(err)) + goto out; + + err = au_opts_verify(sb, sb->s_flags, tmp); + if (unlikely(err)) + goto out; + + /* restore xino */ + if (au_opt_test(tmp, XINO) && !opt_xino) { + xino.file = au_xino_def(sb); + err = PTR_ERR(xino.file); + if (IS_ERR(xino.file)) + goto out; + + err = au_xino_set(sb, &xino, /*remount*/0); + fput(xino.file); + if (unlikely(err)) + goto out; + } + + /* restore udba */ + tmp &= AuOptMask_UDBA; + sbinfo->si_mntflags &= ~AuOptMask_UDBA; + sbinfo->si_mntflags |= tmp; + bbot = au_sbbot(sb); + for (bindex = 0; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + err = au_hnotify_reset_br(tmp, br, br->br_perm); + if (unlikely(err)) + AuIOErr("hnotify failed on br %d, %d, ignored\n", + bindex, err); + /* go on even if err */ + } + if (au_opt_test(tmp, UDBA_HNOTIFY)) { + dir = d_inode(sb->s_root); + au_hn_reset(dir, au_hi_flags(dir, /*isdir*/1) & ~AuHi_XINO); + } + +out: + return err; +} + +int au_opts_remount(struct super_block *sb, struct au_opts *opts) +{ + int err, rerr; + unsigned char no_dreval; + struct inode *dir; + struct au_opt_xino *opt_xino; + struct au_opt *opt; + struct au_sbinfo *sbinfo; + + SiMustWriteLock(sb); + + err = au_dr_opt_flush(sb); + if (unlikely(err)) + goto out; + au_fset_opts(opts->flags, DR_FLUSHED); + + dir = d_inode(sb->s_root); + sbinfo = au_sbi(sb); + opt_xino = NULL; + opt = opts->opt; + while (err >= 0 && opt->type != Opt_tail) { + err = au_opt_simple(sb, opt, opts); + if (!err) + err = au_opt_br(sb, opt, opts); + if (!err) + err = au_opt_xino(sb, opt, &opt_xino, opts); + opt++; + } + if (err > 0) + err = 0; + AuTraceErr(err); + /* go on even err */ + + no_dreval = !!au_ftest_si(sbinfo, NO_DREVAL); + rerr = au_opts_verify(sb, opts->sb_flags, /*pending*/0); + if (unlikely(rerr && !err)) + err = rerr; + + if (no_dreval != !!au_ftest_si(sbinfo, NO_DREVAL)) + au_fset_opts(opts->flags, REFRESH_IDOP); + + if (au_ftest_opts(opts->flags, TRUNC_XIB)) { + rerr = au_xib_trunc(sb); + if (unlikely(rerr && !err)) + err = rerr; + } + + /* will be handled by the caller */ + if (!au_ftest_opts(opts->flags, REFRESH) + && (opts->given_udba + || au_opt_test(sbinfo->si_mntflags, XINO) + || au_ftest_opts(opts->flags, REFRESH_IDOP) + )) + au_fset_opts(opts->flags, REFRESH); + + AuDbg("status 0x%x\n", opts->flags); + +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +unsigned int au_opt_udba(struct super_block *sb) +{ + return au_mntflags(sb) & AuOptMask_UDBA; +} diff --git a/fs/aufs/opts.h b/fs/aufs/opts.h new file mode 100644 index 0000000000000..3aa8ed1ccc035 --- /dev/null +++ b/fs/aufs/opts.h @@ -0,0 +1,263 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * mount options/flags + */ + +#ifndef __AUFS_OPTS_H__ +#define __AUFS_OPTS_H__ + +#ifdef __KERNEL__ + +#include +#include +#include + +enum { + Opt_br, + Opt_add, Opt_del, Opt_mod, Opt_append, Opt_prepend, + Opt_idel, Opt_imod, + Opt_dirwh, Opt_rdcache, Opt_rdblk, Opt_rdhash, + Opt_xino, Opt_noxino, + Opt_trunc_xino, Opt_trunc_xino_v, + Opt_trunc_xino_path, Opt_itrunc_xino, + Opt_trunc_xib, + Opt_shwh, + Opt_plink, Opt_list_plink, + Opt_udba, + Opt_dio, + Opt_diropq, Opt_diropq_a, Opt_diropq_w, + Opt_warn_perm, + Opt_wbr_copyup, Opt_wbr_create, + Opt_fhsm_sec, + Opt_verbose, Opt_noverbose, + Opt_sum, Opt_wsum, + Opt_dirperm1, + Opt_dirren, + Opt_acl, + Opt_tail, Opt_ignore, Opt_ignore_silent, Opt_err +}; + +/* ---------------------------------------------------------------------- */ + +/* mount flags */ +#define AuOpt_XINO 1 /* external inode number bitmap + and translation table */ +#define AuOpt_TRUNC_XINO (1 << 1) /* truncate xino files */ +#define AuOpt_UDBA_NONE (1 << 2) /* users direct branch access */ +#define AuOpt_UDBA_REVAL (1 << 3) +#define AuOpt_UDBA_HNOTIFY (1 << 4) +#define AuOpt_SHWH (1 << 5) /* show whiteout */ +#define AuOpt_PLINK (1 << 6) /* pseudo-link */ +#define AuOpt_DIRPERM1 (1 << 7) /* ignore the lower dir's perm + bits */ +#define AuOpt_ALWAYS_DIROPQ (1 << 9) /* policy to creating diropq */ +#define AuOpt_SUM (1 << 10) /* summation for statfs(2) */ +#define AuOpt_SUM_W (1 << 11) /* unimplemented */ +#define AuOpt_WARN_PERM (1 << 12) /* warn when add-branch */ +#define AuOpt_VERBOSE (1 << 13) /* print the cause of error */ +#define AuOpt_DIO (1 << 14) /* direct io */ +#define AuOpt_DIRREN (1 << 15) /* directory rename */ + +#ifndef CONFIG_AUFS_HNOTIFY +#undef AuOpt_UDBA_HNOTIFY +#define AuOpt_UDBA_HNOTIFY 0 +#endif +#ifndef CONFIG_AUFS_DIRREN +#undef AuOpt_DIRREN +#define AuOpt_DIRREN 0 +#endif +#ifndef CONFIG_AUFS_SHWH +#undef AuOpt_SHWH +#define AuOpt_SHWH 0 +#endif + +#define AuOpt_Def (AuOpt_XINO \ + | AuOpt_UDBA_REVAL \ + | AuOpt_PLINK \ + /* | AuOpt_DIRPERM1 */ \ + | AuOpt_WARN_PERM) +#define AuOptMask_UDBA (AuOpt_UDBA_NONE \ + | AuOpt_UDBA_REVAL \ + | AuOpt_UDBA_HNOTIFY) + +#define AuOpt_LkupDirFlags (LOOKUP_FOLLOW | LOOKUP_DIRECTORY) + +#define au_opt_test(flags, name) (flags & AuOpt_##name) +#define au_opt_set(flags, name) do { \ + BUILD_BUG_ON(AuOpt_##name & AuOptMask_UDBA); \ + ((flags) |= AuOpt_##name); \ +} while (0) +#define au_opt_set_udba(flags, name) do { \ + (flags) &= ~AuOptMask_UDBA; \ + ((flags) |= AuOpt_##name); \ +} while (0) +#define au_opt_clr(flags, name) do { \ + ((flags) &= ~AuOpt_##name); \ +} while (0) + +static inline unsigned int au_opts_plink(unsigned int mntflags) +{ +#ifdef CONFIG_PROC_FS + return mntflags; +#else + return mntflags & ~AuOpt_PLINK; +#endif +} + +/* ---------------------------------------------------------------------- */ + +/* policies to select one among multiple writable branches */ +enum { + AuWbrCreate_TDP, /* top down parent */ + AuWbrCreate_RR, /* round robin */ + AuWbrCreate_MFS, /* most free space */ + AuWbrCreate_MFSV, /* mfs with seconds */ + AuWbrCreate_MFSRR, /* mfs then rr */ + AuWbrCreate_MFSRRV, /* mfs then rr with seconds */ + AuWbrCreate_TDMFS, /* top down regardless parent and mfs */ + AuWbrCreate_TDMFSV, /* top down regardless parent and mfs */ + AuWbrCreate_PMFS, /* parent and mfs */ + AuWbrCreate_PMFSV, /* parent and mfs with seconds */ + AuWbrCreate_PMFSRR, /* parent, mfs and round-robin */ + AuWbrCreate_PMFSRRV, /* plus seconds */ + + AuWbrCreate_Def = AuWbrCreate_TDP +}; + +enum { + AuWbrCopyup_TDP, /* top down parent */ + AuWbrCopyup_BUP, /* bottom up parent */ + AuWbrCopyup_BU, /* bottom up */ + + AuWbrCopyup_Def = AuWbrCopyup_TDP +}; + +/* ---------------------------------------------------------------------- */ + +struct file; + +struct au_opt_add { + aufs_bindex_t bindex; + char *pathname; + int perm; + struct path path; +}; + +struct au_opt_del { + char *pathname; + struct path h_path; +}; + +struct au_opt_mod { + char *path; + int perm; + struct dentry *h_root; +}; + +struct au_opt_xino { + char *path; + struct file *file; +}; + +struct au_opt_xino_itrunc { + aufs_bindex_t bindex; +}; + +struct au_opt_wbr_create { + int wbr_create; + int mfs_second; + unsigned long long mfsrr_watermark; +}; + +struct au_opt { + int type; + union { + struct au_opt_xino xino; + struct au_opt_xino_itrunc xino_itrunc; + struct au_opt_add add; + struct au_opt_del del; + struct au_opt_mod mod; + int dirwh; + int rdcache; + unsigned int rdblk; + unsigned int rdhash; + int udba; + struct au_opt_wbr_create wbr_create; + int wbr_copyup; + unsigned int fhsm_second; + bool tf; /* generic flag, true or false */ + }; +}; + +/* opts flags */ +#define AuOpts_REMOUNT 1 +#define AuOpts_REFRESH (1 << 1) +#define AuOpts_TRUNC_XIB (1 << 2) +#define AuOpts_REFRESH_DYAOP (1 << 3) +#define AuOpts_REFRESH_IDOP (1 << 4) +#define AuOpts_DR_FLUSHED (1 << 5) +#define au_ftest_opts(flags, name) ((flags) & AuOpts_##name) +#define au_fset_opts(flags, name) \ + do { (flags) |= AuOpts_##name; } while (0) +#define au_fclr_opts(flags, name) \ + do { (flags) &= ~AuOpts_##name; } while (0) + +#ifndef CONFIG_AUFS_DIRREN +#undef AuOpts_DR_FLUSHED +#define AuOpts_DR_FLUSHED 0 +#endif + +struct au_opts { + struct au_opt *opt; + int max_opt; + + unsigned int given_udba; + unsigned int flags; + unsigned long sb_flags; +}; + +/* ---------------------------------------------------------------------- */ + +/* opts.c */ +int au_br_perm_val(char *perm); +void au_optstr_br_perm(au_br_perm_str_t *str, int perm); +int au_udba_val(char *str); +const char *au_optstr_udba(int udba); +int au_wbr_create_val(char *str, struct au_opt_wbr_create *create); +const char *au_optstr_wbr_create(int wbr_create); +int au_wbr_copyup_val(char *str); +const char *au_optstr_wbr_copyup(int wbr_copyup); + +int au_opt_add(struct au_opt *opt, char *opt_str, unsigned long sb_flags, + aufs_bindex_t bindex); +struct super_block; +int au_opts_verify(struct super_block *sb, unsigned long sb_flags, + unsigned int pending); +int au_opts_mount(struct super_block *sb, struct au_opts *opts); +int au_opts_remount(struct super_block *sb, struct au_opts *opts); + +unsigned int au_opt_udba(struct super_block *sb); + +/* fsctx.c */ +int aufs_fsctx_init(struct fs_context *fc); +extern const struct fs_parameter_spec aufs_fsctx_paramspec[]; + +#endif /* __KERNEL__ */ +#endif /* __AUFS_OPTS_H__ */ diff --git a/fs/aufs/plink.c b/fs/aufs/plink.c new file mode 100644 index 0000000000000..82e51a50191c3 --- /dev/null +++ b/fs/aufs/plink.c @@ -0,0 +1,516 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * pseudo-link + */ + +#include "aufs.h" + +/* + * the pseudo-link maintenance mode. + * during a user process maintains the pseudo-links, + * prohibit adding a new plink and branch manipulation. + * + * Flags + * NOPLM: + * For entry functions which will handle plink, and i_mutex is already held + * in VFS. + * They cannot wait and should return an error at once. + * Callers has to check the error. + * NOPLMW: + * For entry functions which will handle plink, but i_mutex is not held + * in VFS. + * They can wait the plink maintenance mode to finish. + * + * They behave like F_SETLK and F_SETLKW. + * If the caller never handle plink, then both flags are unnecessary. + */ + +int au_plink_maint(struct super_block *sb, int flags) +{ + int err; + pid_t pid, ppid; + struct task_struct *parent, *prev; + struct au_sbinfo *sbi; + + SiMustAnyLock(sb); + + err = 0; + if (!au_opt_test(au_mntflags(sb), PLINK)) + goto out; + + sbi = au_sbi(sb); + pid = sbi->si_plink_maint_pid; + if (!pid || pid == current->pid) + goto out; + + /* todo: it highly depends upon /sbin/mount.aufs */ + prev = NULL; + parent = current; + ppid = 0; + rcu_read_lock(); + while (1) { + parent = rcu_dereference(parent->real_parent); + if (parent == prev) + break; + ppid = task_pid_vnr(parent); + if (pid == ppid) { + rcu_read_unlock(); + goto out; + } + prev = parent; + } + rcu_read_unlock(); + + if (au_ftest_lock(flags, NOPLMW)) { + /* if there is no i_mutex lock in VFS, we don't need to wait */ + /* AuDebugOn(!lockdep_depth(current)); */ + while (sbi->si_plink_maint_pid) { + si_read_unlock(sb); + /* gave up wake_up_bit() */ + wait_event(sbi->si_plink_wq, !sbi->si_plink_maint_pid); + + if (au_ftest_lock(flags, FLUSH)) + au_nwt_flush(&sbi->si_nowait); + si_noflush_read_lock(sb); + } + } else if (au_ftest_lock(flags, NOPLM)) { + AuDbg("ppid %d, pid %d\n", ppid, pid); + err = -EAGAIN; + } + +out: + return err; +} + +void au_plink_maint_leave(struct au_sbinfo *sbinfo) +{ + spin_lock(&sbinfo->si_plink_maint_lock); + sbinfo->si_plink_maint_pid = 0; + spin_unlock(&sbinfo->si_plink_maint_lock); + wake_up_all(&sbinfo->si_plink_wq); +} + +int au_plink_maint_enter(struct super_block *sb) +{ + int err; + struct au_sbinfo *sbinfo; + + err = 0; + sbinfo = au_sbi(sb); + /* make sure i am the only one in this fs */ + si_write_lock(sb, AuLock_FLUSH); + if (au_opt_test(au_mntflags(sb), PLINK)) { + spin_lock(&sbinfo->si_plink_maint_lock); + if (!sbinfo->si_plink_maint_pid) + sbinfo->si_plink_maint_pid = current->pid; + else + err = -EBUSY; + spin_unlock(&sbinfo->si_plink_maint_lock); + } + si_write_unlock(sb); + + return err; +} + +/* ---------------------------------------------------------------------- */ + +#ifdef CONFIG_AUFS_DEBUG +void au_plink_list(struct super_block *sb) +{ + int i; + struct au_sbinfo *sbinfo; + struct hlist_bl_head *hbl; + struct hlist_bl_node *pos; + struct au_icntnr *icntnr; + + SiMustAnyLock(sb); + + sbinfo = au_sbi(sb); + AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); + AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); + + for (i = 0; i < AuPlink_NHASH; i++) { + hbl = sbinfo->si_plink + i; + hlist_bl_lock(hbl); + hlist_bl_for_each_entry(icntnr, pos, hbl, plink) + AuDbg("%lu\n", icntnr->vfs_inode.i_ino); + hlist_bl_unlock(hbl); + } +} +#endif + +/* is the inode pseudo-linked? */ +int au_plink_test(struct inode *inode) +{ + int found, i; + struct au_sbinfo *sbinfo; + struct hlist_bl_head *hbl; + struct hlist_bl_node *pos; + struct au_icntnr *icntnr; + + sbinfo = au_sbi(inode->i_sb); + AuRwMustAnyLock(&sbinfo->si_rwsem); + AuDebugOn(!au_opt_test(au_mntflags(inode->i_sb), PLINK)); + AuDebugOn(au_plink_maint(inode->i_sb, AuLock_NOPLM)); + + found = 0; + i = au_plink_hash(inode->i_ino); + hbl = sbinfo->si_plink + i; + hlist_bl_lock(hbl); + hlist_bl_for_each_entry(icntnr, pos, hbl, plink) + if (&icntnr->vfs_inode == inode) { + found = 1; + break; + } + hlist_bl_unlock(hbl); + return found; +} + +/* ---------------------------------------------------------------------- */ + +/* + * generate a name for plink. + * the file will be stored under AUFS_WH_PLINKDIR. + */ +/* 20 is max digits length of ulong 64 */ +#define PLINK_NAME_LEN ((20 + 1) * 2) + +static int plink_name(char *name, int len, struct inode *inode, + aufs_bindex_t bindex) +{ + int rlen; + struct inode *h_inode; + + h_inode = au_h_iptr(inode, bindex); + rlen = snprintf(name, len, "%lu.%lu", inode->i_ino, h_inode->i_ino); + return rlen; +} + +struct au_do_plink_lkup_args { + struct dentry **errp; + struct qstr *tgtname; + struct path *h_ppath; +}; + +static struct dentry *au_do_plink_lkup(struct qstr *tgtname, + struct path *h_ppath) +{ + struct dentry *h_dentry; + struct inode *h_inode; + + h_inode = d_inode(h_ppath->dentry); + inode_lock_shared_nested(h_inode, AuLsc_I_CHILD2); + h_dentry = vfsub_lkup_one(tgtname, h_ppath); + inode_unlock_shared(h_inode); + + return h_dentry; +} + +static void au_call_do_plink_lkup(void *args) +{ + struct au_do_plink_lkup_args *a = args; + *a->errp = au_do_plink_lkup(a->tgtname, a->h_ppath); +} + +/* lookup the plink-ed @inode under the branch at @bindex */ +struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex) +{ + struct dentry *h_dentry; + struct au_branch *br; + struct path h_ppath; + int wkq_err; + char a[PLINK_NAME_LEN]; + struct qstr tgtname = QSTR_INIT(a, 0); + + AuDebugOn(au_plink_maint(inode->i_sb, AuLock_NOPLM)); + + br = au_sbr(inode->i_sb, bindex); + h_ppath.dentry = br->br_wbr->wbr_plink; + h_ppath.mnt = au_br_mnt(br); + tgtname.len = plink_name(a, sizeof(a), inode, bindex); + + if (!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) { + struct au_do_plink_lkup_args args = { + .errp = &h_dentry, + .tgtname = &tgtname, + .h_ppath = &h_ppath + }; + + wkq_err = au_wkq_wait(au_call_do_plink_lkup, &args); + if (unlikely(wkq_err)) + h_dentry = ERR_PTR(wkq_err); + } else + h_dentry = au_do_plink_lkup(&tgtname, &h_ppath); + + return h_dentry; +} + +/* create a pseudo-link */ +static int do_whplink(struct qstr *tgt, struct path *h_ppath, + struct dentry *h_dentry) +{ + int err; + struct path h_path; + struct inode *h_dir, *delegated; + + h_dir = d_inode(h_ppath->dentry); + inode_lock_nested(h_dir, AuLsc_I_CHILD2); + h_path.mnt = h_ppath->mnt; +again: + h_path.dentry = vfsub_lkup_one(tgt, h_ppath); + err = PTR_ERR(h_path.dentry); + if (IS_ERR(h_path.dentry)) + goto out; + + err = 0; + /* wh.plink dir is not monitored */ + /* todo: is it really safe? */ + if (d_is_positive(h_path.dentry) + && d_inode(h_path.dentry) != d_inode(h_dentry)) { + delegated = NULL; + err = vfsub_unlink(h_dir, &h_path, &delegated, /*force*/0); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal unlink\n"); + iput(delegated); + } + dput(h_path.dentry); + h_path.dentry = NULL; + if (!err) + goto again; + } + if (!err && d_is_negative(h_path.dentry)) { + delegated = NULL; + err = vfsub_link(h_dentry, h_dir, &h_path, &delegated); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal link\n"); + iput(delegated); + } + } + dput(h_path.dentry); + +out: + inode_unlock(h_dir); + return err; +} + +struct do_whplink_args { + int *errp; + struct qstr *tgt; + struct path *h_ppath; + struct dentry *h_dentry; +}; + +static void call_do_whplink(void *args) +{ + struct do_whplink_args *a = args; + *a->errp = do_whplink(a->tgt, a->h_ppath, a->h_dentry); +} + +static int whplink(struct dentry *h_dentry, struct inode *inode, + aufs_bindex_t bindex) +{ + int err, wkq_err; + struct au_branch *br; + struct au_wbr *wbr; + struct path h_ppath; + char a[PLINK_NAME_LEN]; + struct qstr tgtname = QSTR_INIT(a, 0); + + br = au_sbr(inode->i_sb, bindex); + wbr = br->br_wbr; + h_ppath.dentry = wbr->wbr_plink; + h_ppath.mnt = au_br_mnt(br); + tgtname.len = plink_name(a, sizeof(a), inode, bindex); + + /* always superio. */ + if (!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) { + struct do_whplink_args args = { + .errp = &err, + .tgt = &tgtname, + .h_ppath = &h_ppath, + .h_dentry = h_dentry + }; + wkq_err = au_wkq_wait(call_do_whplink, &args); + if (unlikely(wkq_err)) + err = wkq_err; + } else + err = do_whplink(&tgtname, &h_ppath, h_dentry); + + return err; +} + +/* + * create a new pseudo-link for @h_dentry on @bindex. + * the linked inode is held in aufs @inode. + */ +void au_plink_append(struct inode *inode, aufs_bindex_t bindex, + struct dentry *h_dentry) +{ + struct super_block *sb; + struct au_sbinfo *sbinfo; + struct hlist_bl_head *hbl; + struct hlist_bl_node *pos; + struct au_icntnr *icntnr; + int found, err, cnt, i; + + sb = inode->i_sb; + sbinfo = au_sbi(sb); + AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); + AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); + + found = au_plink_test(inode); + if (found) + return; + + i = au_plink_hash(inode->i_ino); + hbl = sbinfo->si_plink + i; + au_igrab(inode); + + hlist_bl_lock(hbl); + hlist_bl_for_each_entry(icntnr, pos, hbl, plink) { + if (&icntnr->vfs_inode == inode) { + found = 1; + break; + } + } + if (!found) { + icntnr = container_of(inode, struct au_icntnr, vfs_inode); + hlist_bl_add_head(&icntnr->plink, hbl); + } + hlist_bl_unlock(hbl); + if (!found) { + cnt = au_hbl_count(hbl); +#define msg "unexpectedly unbalanced or too many pseudo-links" + if (cnt > AUFS_PLINK_WARN) + AuWarn1(msg ", %d\n", cnt); +#undef msg + err = whplink(h_dentry, inode, bindex); + if (unlikely(err)) { + pr_warn("err %d, damaged pseudo link.\n", err); + au_hbl_del(&icntnr->plink, hbl); + iput(&icntnr->vfs_inode); + } + } else + iput(&icntnr->vfs_inode); +} + +/* free all plinks */ +void au_plink_put(struct super_block *sb, int verbose) +{ + int i, warned; + struct au_sbinfo *sbinfo; + struct hlist_bl_head *hbl; + struct hlist_bl_node *pos, *tmp; + struct au_icntnr *icntnr; + + SiMustWriteLock(sb); + + sbinfo = au_sbi(sb); + AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); + AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); + + /* no spin_lock since sbinfo is write-locked */ + warned = 0; + for (i = 0; i < AuPlink_NHASH; i++) { + hbl = sbinfo->si_plink + i; + if (!warned && verbose && !hlist_bl_empty(hbl)) { + pr_warn("pseudo-link is not flushed"); + warned = 1; + } + hlist_bl_for_each_entry_safe(icntnr, pos, tmp, hbl, plink) + iput(&icntnr->vfs_inode); + INIT_HLIST_BL_HEAD(hbl); + } +} + +void au_plink_clean(struct super_block *sb, int verbose) +{ + struct dentry *root; + + root = sb->s_root; + aufs_write_lock(root); + if (au_opt_test(au_mntflags(sb), PLINK)) + au_plink_put(sb, verbose); + aufs_write_unlock(root); +} + +static int au_plink_do_half_refresh(struct inode *inode, aufs_bindex_t br_id) +{ + int do_put; + aufs_bindex_t btop, bbot, bindex; + + do_put = 0; + btop = au_ibtop(inode); + bbot = au_ibbot(inode); + if (btop >= 0) { + for (bindex = btop; bindex <= bbot; bindex++) { + if (!au_h_iptr(inode, bindex) + || au_ii_br_id(inode, bindex) != br_id) + continue; + au_set_h_iptr(inode, bindex, NULL, 0); + do_put = 1; + break; + } + if (do_put) + for (bindex = btop; bindex <= bbot; bindex++) + if (au_h_iptr(inode, bindex)) { + do_put = 0; + break; + } + } else + do_put = 1; + + return do_put; +} + +/* free the plinks on a branch specified by @br_id */ +void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id) +{ + struct au_sbinfo *sbinfo; + struct hlist_bl_head *hbl; + struct hlist_bl_node *pos, *tmp; + struct au_icntnr *icntnr; + struct inode *inode; + int i, do_put; + + SiMustWriteLock(sb); + + sbinfo = au_sbi(sb); + AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); + AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); + + /* no bit_lock since sbinfo is write-locked */ + for (i = 0; i < AuPlink_NHASH; i++) { + hbl = sbinfo->si_plink + i; + hlist_bl_for_each_entry_safe(icntnr, pos, tmp, hbl, plink) { + inode = au_igrab(&icntnr->vfs_inode); + ii_write_lock_child(inode); + do_put = au_plink_do_half_refresh(inode, br_id); + if (do_put) { + hlist_bl_del(&icntnr->plink); + iput(inode); + } + ii_write_unlock(inode); + iput(inode); + } + } +} diff --git a/fs/aufs/poll.c b/fs/aufs/poll.c new file mode 100644 index 0000000000000..d1081a7f02f24 --- /dev/null +++ b/fs/aufs/poll.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * poll operation + * There is only one filesystem which implements ->poll operation, currently. + */ + +#include "aufs.h" + +__poll_t aufs_poll(struct file *file, struct poll_table_struct *pt) +{ + __poll_t mask; + struct file *h_file; + struct super_block *sb; + + /* We should pretend an error happened. */ + mask = EPOLLERR /* | EPOLLIN | EPOLLOUT */; + sb = file->f_path.dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); + + h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0); + if (IS_ERR(h_file)) { + AuDbg("h_file %ld\n", PTR_ERR(h_file)); + goto out; + } + + mask = vfs_poll(h_file, pt); + fput(h_file); /* instead of au_read_post() */ + +out: + si_read_unlock(sb); + if (mask & EPOLLERR) + AuDbg("mask 0x%x\n", mask); + return mask; +} diff --git a/fs/aufs/posix_acl.c b/fs/aufs/posix_acl.c new file mode 100644 index 0000000000000..161f9fa8609bd --- /dev/null +++ b/fs/aufs/posix_acl.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2014-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * posix acl operations + */ + +#include +#include "aufs.h" + +struct posix_acl *aufs_get_acl(struct inode *inode, int type) +{ + struct posix_acl *acl; + int err; + aufs_bindex_t bindex; + struct inode *h_inode; + struct super_block *sb; + + acl = NULL; + sb = inode->i_sb; + si_read_lock(sb, AuLock_FLUSH); + ii_read_lock_child(inode); + if (!(sb->s_flags & SB_POSIXACL)) + goto out; + + bindex = au_ibtop(inode); + h_inode = au_h_iptr(inode, bindex); + if (unlikely(!h_inode + || ((h_inode->i_mode & S_IFMT) + != (inode->i_mode & S_IFMT)))) { + err = au_busy_or_stale(); + acl = ERR_PTR(err); + goto out; + } + + /* always topmost only */ + acl = get_acl(h_inode, type); + if (IS_ERR(acl)) + forget_cached_acl(inode, type); + else + set_cached_acl(inode, type, acl); + +out: + ii_read_unlock(inode); + si_read_unlock(sb); + + AuTraceErrPtr(acl); + return acl; +} + +int aufs_set_acl(struct inode *inode, struct posix_acl *acl, int type) +{ + int err; + ssize_t ssz; + struct dentry *dentry; + struct au_sxattr arg = { + .type = AU_ACL_SET, + .u.acl_set = { + .acl = acl, + .type = type + }, + }; + + IMustLock(inode); + + if (inode->i_ino == AUFS_ROOT_INO) + dentry = dget(inode->i_sb->s_root); + else { + dentry = d_find_alias(inode); + if (!dentry) + dentry = d_find_any_alias(inode); + if (!dentry) { + pr_warn("cannot handle this inode, " + "please report to aufs-users ML\n"); + err = -ENOENT; + goto out; + } + } + + ssz = au_sxattr(dentry, inode, &arg); + /* forget even it if succeeds since the branch might set differently */ + forget_cached_acl(inode, type); + dput(dentry); + err = ssz; + if (ssz >= 0) + err = 0; + +out: + return err; +} diff --git a/fs/aufs/procfs.c b/fs/aufs/procfs.c new file mode 100644 index 0000000000000..303558e927366 --- /dev/null +++ b/fs/aufs/procfs.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2010-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * procfs interfaces + */ + +#include +#include "aufs.h" + +static int au_procfs_plm_release(struct inode *inode, struct file *file) +{ + struct au_sbinfo *sbinfo; + + sbinfo = file->private_data; + if (sbinfo) { + au_plink_maint_leave(sbinfo); + kobject_put(&sbinfo->si_kobj); + } + + return 0; +} + +static void au_procfs_plm_write_clean(struct file *file) +{ + struct au_sbinfo *sbinfo; + + sbinfo = file->private_data; + if (sbinfo) + au_plink_clean(sbinfo->si_sb, /*verbose*/0); +} + +static int au_procfs_plm_write_si(struct file *file, unsigned long id) +{ + int err; + struct super_block *sb; + struct au_sbinfo *sbinfo; + struct hlist_bl_node *pos; + + err = -EBUSY; + if (unlikely(file->private_data)) + goto out; + + sb = NULL; + /* don't use au_sbilist_lock() here */ + hlist_bl_lock(&au_sbilist); + hlist_bl_for_each_entry(sbinfo, pos, &au_sbilist, si_list) + if (id == sysaufs_si_id(sbinfo)) { + if (kobject_get_unless_zero(&sbinfo->si_kobj)) + sb = sbinfo->si_sb; + break; + } + hlist_bl_unlock(&au_sbilist); + + err = -EINVAL; + if (unlikely(!sb)) + goto out; + + err = au_plink_maint_enter(sb); + if (!err) + /* keep kobject_get() */ + file->private_data = sbinfo; + else + kobject_put(&sbinfo->si_kobj); +out: + return err; +} + +/* + * Accept a valid "si=xxxx" only. + * Once it is accepted successfully, accept "clean" too. + */ +static ssize_t au_procfs_plm_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + ssize_t err; + unsigned long id; + /* last newline is allowed */ + char buf[3 + sizeof(unsigned long) * 2 + 1]; + + err = -EACCES; + if (unlikely(!capable(CAP_SYS_ADMIN))) + goto out; + + err = -EINVAL; + if (unlikely(count > sizeof(buf))) + goto out; + + err = copy_from_user(buf, ubuf, count); + if (unlikely(err)) { + err = -EFAULT; + goto out; + } + buf[count] = 0; + + err = -EINVAL; + if (!strcmp("clean", buf)) { + au_procfs_plm_write_clean(file); + goto out_success; + } else if (unlikely(strncmp("si=", buf, 3))) + goto out; + + err = kstrtoul(buf + 3, 16, &id); + if (unlikely(err)) + goto out; + + err = au_procfs_plm_write_si(file, id); + if (unlikely(err)) + goto out; + +out_success: + err = count; /* success */ +out: + return err; +} + +static const struct proc_ops au_procfs_plm_op = { + .proc_write = au_procfs_plm_write, + .proc_release = au_procfs_plm_release +}; + +/* ---------------------------------------------------------------------- */ + +static struct proc_dir_entry *au_procfs_dir; + +void au_procfs_fin(void) +{ + remove_proc_entry(AUFS_PLINK_MAINT_NAME, au_procfs_dir); + remove_proc_entry(AUFS_PLINK_MAINT_DIR, NULL); +} + +int __init au_procfs_init(void) +{ + int err; + struct proc_dir_entry *entry; + + err = -ENOMEM; + au_procfs_dir = proc_mkdir(AUFS_PLINK_MAINT_DIR, NULL); + if (unlikely(!au_procfs_dir)) + goto out; + + entry = proc_create(AUFS_PLINK_MAINT_NAME, S_IFREG | 0200, + au_procfs_dir, &au_procfs_plm_op); + if (unlikely(!entry)) + goto out_dir; + + err = 0; + goto out; /* success */ + + +out_dir: + remove_proc_entry(AUFS_PLINK_MAINT_DIR, NULL); +out: + return err; +} diff --git a/fs/aufs/rdu.c b/fs/aufs/rdu.c new file mode 100644 index 0000000000000..54aa07ba5b54f --- /dev/null +++ b/fs/aufs/rdu.c @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * readdir in userspace. + */ + +#include +#include +#include +#include "aufs.h" + +/* bits for struct aufs_rdu.flags */ +#define AuRdu_CALLED 1 +#define AuRdu_CONT (1 << 1) +#define AuRdu_FULL (1 << 2) +#define au_ftest_rdu(flags, name) ((flags) & AuRdu_##name) +#define au_fset_rdu(flags, name) \ + do { (flags) |= AuRdu_##name; } while (0) +#define au_fclr_rdu(flags, name) \ + do { (flags) &= ~AuRdu_##name; } while (0) + +struct au_rdu_arg { + struct dir_context ctx; + struct aufs_rdu *rdu; + union au_rdu_ent_ul ent; + unsigned long end; + + struct super_block *sb; + int err; +}; + +static int au_rdu_fill(struct dir_context *ctx, const char *name, int nlen, + loff_t offset, u64 h_ino, unsigned int d_type) +{ + int err, len; + struct au_rdu_arg *arg = container_of(ctx, struct au_rdu_arg, ctx); + struct aufs_rdu *rdu = arg->rdu; + struct au_rdu_ent ent; + + err = 0; + arg->err = 0; + au_fset_rdu(rdu->cookie.flags, CALLED); + len = au_rdu_len(nlen); + if (arg->ent.ul + len < arg->end) { + ent.ino = h_ino; + ent.bindex = rdu->cookie.bindex; + ent.type = d_type; + ent.nlen = nlen; + if (unlikely(nlen > AUFS_MAX_NAMELEN)) + ent.type = DT_UNKNOWN; + + /* unnecessary to support mmap_sem since this is a dir */ + err = -EFAULT; + if (copy_to_user(arg->ent.e, &ent, sizeof(ent))) + goto out; + if (copy_to_user(arg->ent.e->name, name, nlen)) + goto out; + /* the terminating NULL */ + if (__put_user(0, arg->ent.e->name + nlen)) + goto out; + err = 0; + /* AuDbg("%p, %.*s\n", arg->ent.p, nlen, name); */ + arg->ent.ul += len; + rdu->rent++; + } else { + err = -EFAULT; + au_fset_rdu(rdu->cookie.flags, FULL); + rdu->full = 1; + rdu->tail = arg->ent; + } + +out: + /* AuTraceErr(err); */ + return err; +} + +static int au_rdu_do(struct file *h_file, struct au_rdu_arg *arg) +{ + int err; + loff_t offset; + struct au_rdu_cookie *cookie = &arg->rdu->cookie; + + /* we don't have to care (FMODE_32BITHASH | FMODE_64BITHASH) for ext4 */ + offset = vfsub_llseek(h_file, cookie->h_pos, SEEK_SET); + err = offset; + if (unlikely(offset != cookie->h_pos)) + goto out; + + err = 0; + do { + arg->err = 0; + au_fclr_rdu(cookie->flags, CALLED); + /* smp_mb(); */ + err = vfsub_iterate_dir(h_file, &arg->ctx); + if (err >= 0) + err = arg->err; + } while (!err + && au_ftest_rdu(cookie->flags, CALLED) + && !au_ftest_rdu(cookie->flags, FULL)); + cookie->h_pos = h_file->f_pos; + +out: + AuTraceErr(err); + return err; +} + +static int au_rdu(struct file *file, struct aufs_rdu *rdu) +{ + int err; + aufs_bindex_t bbot; + struct au_rdu_arg arg = { + .ctx = { + .actor = au_rdu_fill + } + }; + struct dentry *dentry; + struct inode *inode; + struct file *h_file; + struct au_rdu_cookie *cookie = &rdu->cookie; + + /* VERIFY_WRITE */ + err = !access_ok(rdu->ent.e, rdu->sz); + if (unlikely(err)) { + err = -EFAULT; + AuTraceErr(err); + goto out; + } + rdu->rent = 0; + rdu->tail = rdu->ent; + rdu->full = 0; + arg.rdu = rdu; + arg.ent = rdu->ent; + arg.end = arg.ent.ul; + arg.end += rdu->sz; + + err = -ENOTDIR; + if (unlikely(!file->f_op->iterate && !file->f_op->iterate_shared)) + goto out; + + err = security_file_permission(file, MAY_READ); + AuTraceErr(err); + if (unlikely(err)) + goto out; + + dentry = file->f_path.dentry; + inode = d_inode(dentry); + inode_lock_shared(inode); + + arg.sb = inode->i_sb; + err = si_read_lock(arg.sb, AuLock_FLUSH | AuLock_NOPLM); + if (unlikely(err)) + goto out_mtx; + err = au_alive_dir(dentry); + if (unlikely(err)) + goto out_si; + /* todo: reval? */ + fi_read_lock(file); + + err = -EAGAIN; + if (unlikely(au_ftest_rdu(cookie->flags, CONT) + && cookie->generation != au_figen(file))) + goto out_unlock; + + err = 0; + if (!rdu->blk) { + rdu->blk = au_sbi(arg.sb)->si_rdblk; + if (!rdu->blk) + rdu->blk = au_dir_size(file, /*dentry*/NULL); + } + bbot = au_fbtop(file); + if (cookie->bindex < bbot) + cookie->bindex = bbot; + bbot = au_fbbot_dir(file); + /* AuDbg("b%d, b%d\n", cookie->bindex, bbot); */ + for (; !err && cookie->bindex <= bbot; + cookie->bindex++, cookie->h_pos = 0) { + h_file = au_hf_dir(file, cookie->bindex); + if (!h_file) + continue; + + au_fclr_rdu(cookie->flags, FULL); + err = au_rdu_do(h_file, &arg); + AuTraceErr(err); + if (unlikely(au_ftest_rdu(cookie->flags, FULL) || err)) + break; + } + AuDbg("rent %llu\n", rdu->rent); + + if (!err && !au_ftest_rdu(cookie->flags, CONT)) { + rdu->shwh = !!au_opt_test(au_sbi(arg.sb)->si_mntflags, SHWH); + au_fset_rdu(cookie->flags, CONT); + cookie->generation = au_figen(file); + } + + ii_read_lock_child(inode); + fsstack_copy_attr_atime(inode, au_h_iptr(inode, au_ibtop(inode))); + ii_read_unlock(inode); + +out_unlock: + fi_read_unlock(file); +out_si: + si_read_unlock(arg.sb); +out_mtx: + inode_unlock_shared(inode); +out: + AuTraceErr(err); + return err; +} + +static int au_rdu_ino(struct file *file, struct aufs_rdu *rdu) +{ + int err; + ino_t ino; + unsigned long long nent; + union au_rdu_ent_ul *u; + struct au_rdu_ent ent; + struct super_block *sb; + + err = 0; + nent = rdu->nent; + u = &rdu->ent; + sb = file->f_path.dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH); + while (nent-- > 0) { + /* unnecessary to support mmap_sem since this is a dir */ + err = copy_from_user(&ent, u->e, sizeof(ent)); + if (!err) + /* VERIFY_WRITE */ + err = !access_ok(&u->e->ino, sizeof(ino)); + if (unlikely(err)) { + err = -EFAULT; + AuTraceErr(err); + break; + } + + /* AuDbg("b%d, i%llu\n", ent.bindex, ent.ino); */ + if (!ent.wh) + err = au_ino(sb, ent.bindex, ent.ino, ent.type, &ino); + else + err = au_wh_ino(sb, ent.bindex, ent.ino, ent.type, + &ino); + if (unlikely(err)) { + AuTraceErr(err); + break; + } + + err = __put_user(ino, &u->e->ino); + if (unlikely(err)) { + err = -EFAULT; + AuTraceErr(err); + break; + } + u->ul += au_rdu_len(ent.nlen); + } + si_read_unlock(sb); + + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int au_rdu_verify(struct aufs_rdu *rdu) +{ + AuDbg("rdu{%llu, %p, %u | %u | %llu, %u, %u | " + "%llu, b%d, 0x%x, g%u}\n", + rdu->sz, rdu->ent.e, rdu->verify[AufsCtlRduV_SZ], + rdu->blk, + rdu->rent, rdu->shwh, rdu->full, + rdu->cookie.h_pos, rdu->cookie.bindex, rdu->cookie.flags, + rdu->cookie.generation); + + if (rdu->verify[AufsCtlRduV_SZ] == sizeof(*rdu)) + return 0; + + AuDbg("%u:%u\n", + rdu->verify[AufsCtlRduV_SZ], (unsigned int)sizeof(*rdu)); + return -EINVAL; +} + +long au_rdu_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + long err, e; + struct aufs_rdu rdu; + void __user *p = (void __user *)arg; + + err = copy_from_user(&rdu, p, sizeof(rdu)); + if (unlikely(err)) { + err = -EFAULT; + AuTraceErr(err); + goto out; + } + err = au_rdu_verify(&rdu); + if (unlikely(err)) + goto out; + + switch (cmd) { + case AUFS_CTL_RDU: + err = au_rdu(file, &rdu); + if (unlikely(err)) + break; + + e = copy_to_user(p, &rdu, sizeof(rdu)); + if (unlikely(e)) { + err = -EFAULT; + AuTraceErr(err); + } + break; + case AUFS_CTL_RDU_INO: + err = au_rdu_ino(file, &rdu); + break; + + default: + /* err = -ENOTTY; */ + err = -EINVAL; + } + +out: + AuTraceErr(err); + return err; +} + +#ifdef CONFIG_COMPAT +long au_rdu_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + long err, e; + struct aufs_rdu rdu; + void __user *p = compat_ptr(arg); + + /* todo: get_user()? */ + err = copy_from_user(&rdu, p, sizeof(rdu)); + if (unlikely(err)) { + err = -EFAULT; + AuTraceErr(err); + goto out; + } + rdu.ent.e = compat_ptr(rdu.ent.ul); + err = au_rdu_verify(&rdu); + if (unlikely(err)) + goto out; + + switch (cmd) { + case AUFS_CTL_RDU: + err = au_rdu(file, &rdu); + if (unlikely(err)) + break; + + rdu.ent.ul = ptr_to_compat(rdu.ent.e); + rdu.tail.ul = ptr_to_compat(rdu.tail.e); + e = copy_to_user(p, &rdu, sizeof(rdu)); + if (unlikely(e)) { + err = -EFAULT; + AuTraceErr(err); + } + break; + case AUFS_CTL_RDU_INO: + err = au_rdu_ino(file, &rdu); + break; + + default: + /* err = -ENOTTY; */ + err = -EINVAL; + } + +out: + AuTraceErr(err); + return err; +} +#endif diff --git a/fs/aufs/rwsem.h b/fs/aufs/rwsem.h new file mode 100644 index 0000000000000..36c1f90e25881 --- /dev/null +++ b/fs/aufs/rwsem.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * simple read-write semaphore wrappers + */ + +#ifndef __AUFS_RWSEM_H__ +#define __AUFS_RWSEM_H__ + +#ifdef __KERNEL__ + +#include "debug.h" + +/* in the future, the name 'au_rwsem' will be totally gone */ +#define au_rwsem rw_semaphore + +/* to debug easier, do not make them inlined functions */ +#define AuRwMustNoWaiters(rw) AuDebugOn(rwsem_is_contended(rw)) +/* rwsem_is_locked() is unusable */ +#define AuRwMustReadLock(rw) AuDebugOn(IS_ENABLED(CONFIG_LOCKDEP) \ + && !lockdep_recursing(current) \ + && debug_locks \ + && !lockdep_is_held_type(rw, 1)) +#define AuRwMustWriteLock(rw) AuDebugOn(IS_ENABLED(CONFIG_LOCKDEP) \ + && !lockdep_recursing(current) \ + && debug_locks \ + && !lockdep_is_held_type(rw, 0)) +#define AuRwMustAnyLock(rw) AuDebugOn(IS_ENABLED(CONFIG_LOCKDEP) \ + && !lockdep_recursing(current) \ + && debug_locks \ + && !lockdep_is_held(rw)) +#define AuRwDestroy(rw) AuDebugOn(IS_ENABLED(CONFIG_LOCKDEP) \ + && !lockdep_recursing(current) \ + && debug_locks \ + && lockdep_is_held(rw)) + +#define au_rw_init(rw) init_rwsem(rw) + +#define au_rw_init_wlock(rw) do { \ + au_rw_init(rw); \ + down_write(rw); \ + } while (0) + +#define au_rw_init_wlock_nested(rw, lsc) do { \ + au_rw_init(rw); \ + down_write_nested(rw, lsc); \ + } while (0) + +#define au_rw_read_lock(rw) down_read(rw) +#define au_rw_read_lock_nested(rw, lsc) down_read_nested(rw, lsc) +#define au_rw_read_unlock(rw) up_read(rw) +#define au_rw_dgrade_lock(rw) downgrade_write(rw) +#define au_rw_write_lock(rw) down_write(rw) +#define au_rw_write_lock_nested(rw, lsc) down_write_nested(rw, lsc) +#define au_rw_write_unlock(rw) up_write(rw) +/* why is not _nested version defined? */ +#define au_rw_read_trylock(rw) down_read_trylock(rw) +#define au_rw_write_trylock(rw) down_write_trylock(rw) + +#endif /* __KERNEL__ */ +#endif /* __AUFS_RWSEM_H__ */ diff --git a/fs/aufs/sbinfo.c b/fs/aufs/sbinfo.c new file mode 100644 index 0000000000000..0169428a42980 --- /dev/null +++ b/fs/aufs/sbinfo.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * superblock private data + */ + +#include +#include "aufs.h" + +/* + * they are necessary regardless sysfs is disabled. + */ +void au_si_free(struct kobject *kobj) +{ + int i; + struct au_sbinfo *sbinfo; + char *locked __maybe_unused; /* debug only */ + + sbinfo = container_of(kobj, struct au_sbinfo, si_kobj); + for (i = 0; i < AuPlink_NHASH; i++) + AuDebugOn(!hlist_bl_empty(sbinfo->si_plink + i)); + AuDebugOn(atomic_read(&sbinfo->si_nowait.nw_len)); + + AuLCntZero(au_lcnt_read(&sbinfo->si_ninodes, /*do_rev*/0)); + au_lcnt_fin(&sbinfo->si_ninodes, /*do_sync*/0); + AuLCntZero(au_lcnt_read(&sbinfo->si_nfiles, /*do_rev*/0)); + au_lcnt_fin(&sbinfo->si_nfiles, /*do_sync*/0); + + dbgaufs_si_fin(sbinfo); + au_rw_write_lock(&sbinfo->si_rwsem); + au_br_free(sbinfo); + au_rw_write_unlock(&sbinfo->si_rwsem); + + au_kfree_try_rcu(sbinfo->si_branch); + mutex_destroy(&sbinfo->si_xib_mtx); + AuRwDestroy(&sbinfo->si_rwsem); + + au_lcnt_wait_for_fin(&sbinfo->si_ninodes); + /* si_nfiles is waited too */ + au_kfree_rcu(sbinfo); +} + +struct au_sbinfo *au_si_alloc(struct super_block *sb) +{ + struct au_sbinfo *sbinfo; + int err, i; + + err = -ENOMEM; + sbinfo = kzalloc(sizeof(*sbinfo), GFP_NOFS); + if (unlikely(!sbinfo)) + goto out; + + /* will be reallocated separately */ + sbinfo->si_branch = kzalloc(sizeof(*sbinfo->si_branch), GFP_NOFS); + if (unlikely(!sbinfo->si_branch)) + goto out_sbinfo; + + err = sysaufs_si_init(sbinfo); + if (!err) { + dbgaufs_si_null(sbinfo); + err = dbgaufs_si_init(sbinfo); + if (unlikely(err)) + kobject_put(&sbinfo->si_kobj); + } + if (unlikely(err)) + goto out_br; + + au_nwt_init(&sbinfo->si_nowait); + au_rw_init_wlock(&sbinfo->si_rwsem); + + au_lcnt_init(&sbinfo->si_ninodes, /*release*/NULL); + au_lcnt_init(&sbinfo->si_nfiles, /*release*/NULL); + + sbinfo->si_bbot = -1; + sbinfo->si_last_br_id = AUFS_BRANCH_MAX / 2; + + sbinfo->si_wbr_copyup = AuWbrCopyup_Def; + sbinfo->si_wbr_create = AuWbrCreate_Def; + sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + sbinfo->si_wbr_copyup; + sbinfo->si_wbr_create_ops = au_wbr_create_ops + sbinfo->si_wbr_create; + + au_fhsm_init(sbinfo); + + sbinfo->si_mntflags = au_opts_plink(AuOpt_Def); + + sbinfo->si_xino_jiffy = jiffies; + sbinfo->si_xino_expire + = msecs_to_jiffies(AUFS_XINO_DEF_SEC * MSEC_PER_SEC); + mutex_init(&sbinfo->si_xib_mtx); + /* leave si_xib_last_pindex and si_xib_next_bit */ + + INIT_HLIST_BL_HEAD(&sbinfo->si_aopen); + + sbinfo->si_rdcache = msecs_to_jiffies(AUFS_RDCACHE_DEF * MSEC_PER_SEC); + sbinfo->si_rdblk = AUFS_RDBLK_DEF; + sbinfo->si_rdhash = AUFS_RDHASH_DEF; + sbinfo->si_dirwh = AUFS_DIRWH_DEF; + + for (i = 0; i < AuPlink_NHASH; i++) + INIT_HLIST_BL_HEAD(sbinfo->si_plink + i); + init_waitqueue_head(&sbinfo->si_plink_wq); + spin_lock_init(&sbinfo->si_plink_maint_lock); + + INIT_HLIST_BL_HEAD(&sbinfo->si_files); + + /* with getattr by default */ + sbinfo->si_iop_array = aufs_iop; + + /* leave other members for sysaufs and si_mnt. */ + sbinfo->si_sb = sb; + if (sb) { + sb->s_fs_info = sbinfo; + si_pid_set(sb); + } + return sbinfo; /* success */ + +out_br: + au_kfree_try_rcu(sbinfo->si_branch); +out_sbinfo: + au_kfree_rcu(sbinfo); +out: + return ERR_PTR(err); +} + +int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr, int may_shrink) +{ + int err, sz; + struct au_branch **brp; + + AuRwMustWriteLock(&sbinfo->si_rwsem); + + err = -ENOMEM; + sz = sizeof(*brp) * (sbinfo->si_bbot + 1); + if (unlikely(!sz)) + sz = sizeof(*brp); + brp = au_kzrealloc(sbinfo->si_branch, sz, sizeof(*brp) * nbr, GFP_NOFS, + may_shrink); + if (brp) { + sbinfo->si_branch = brp; + err = 0; + } + + return err; +} + +/* ---------------------------------------------------------------------- */ + +unsigned int au_sigen_inc(struct super_block *sb) +{ + unsigned int gen; + struct inode *inode; + + SiMustWriteLock(sb); + + gen = ++au_sbi(sb)->si_generation; + au_update_digen(sb->s_root); + inode = d_inode(sb->s_root); + au_update_iigen(inode, /*half*/0); + inode_inc_iversion(inode); + return gen; +} + +aufs_bindex_t au_new_br_id(struct super_block *sb) +{ + aufs_bindex_t br_id; + int i; + struct au_sbinfo *sbinfo; + + SiMustWriteLock(sb); + + sbinfo = au_sbi(sb); + for (i = 0; i <= AUFS_BRANCH_MAX; i++) { + br_id = ++sbinfo->si_last_br_id; + AuDebugOn(br_id < 0); + if (br_id && au_br_index(sb, br_id) < 0) + return br_id; + } + + return -1; +} + +/* ---------------------------------------------------------------------- */ + +/* it is ok that new 'nwt' tasks are appended while we are sleeping */ +int si_read_lock(struct super_block *sb, int flags) +{ + int err; + + err = 0; + if (au_ftest_lock(flags, FLUSH)) + au_nwt_flush(&au_sbi(sb)->si_nowait); + + si_noflush_read_lock(sb); + err = au_plink_maint(sb, flags); + if (unlikely(err)) + si_read_unlock(sb); + + return err; +} + +int si_write_lock(struct super_block *sb, int flags) +{ + int err; + + if (au_ftest_lock(flags, FLUSH)) + au_nwt_flush(&au_sbi(sb)->si_nowait); + + si_noflush_write_lock(sb); + err = au_plink_maint(sb, flags); + if (unlikely(err)) + si_write_unlock(sb); + + return err; +} + +/* dentry and super_block lock. call at entry point */ +int aufs_read_lock(struct dentry *dentry, int flags) +{ + int err; + struct super_block *sb; + + sb = dentry->d_sb; + err = si_read_lock(sb, flags); + if (unlikely(err)) + goto out; + + if (au_ftest_lock(flags, DW)) + di_write_lock_child(dentry); + else + di_read_lock_child(dentry, flags); + + if (au_ftest_lock(flags, GEN)) { + err = au_digen_test(dentry, au_sigen(sb)); + if (!au_opt_test(au_mntflags(sb), UDBA_NONE)) + AuDebugOn(!err && au_dbrange_test(dentry)); + else if (!err) + err = au_dbrange_test(dentry); + if (unlikely(err)) + aufs_read_unlock(dentry, flags); + } + +out: + return err; +} + +void aufs_read_unlock(struct dentry *dentry, int flags) +{ + if (au_ftest_lock(flags, DW)) + di_write_unlock(dentry); + else + di_read_unlock(dentry, flags); + si_read_unlock(dentry->d_sb); +} + +void aufs_write_lock(struct dentry *dentry) +{ + si_write_lock(dentry->d_sb, AuLock_FLUSH | AuLock_NOPLMW); + di_write_lock_child(dentry); +} + +void aufs_write_unlock(struct dentry *dentry) +{ + di_write_unlock(dentry); + si_write_unlock(dentry->d_sb); +} + +int aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags) +{ + int err; + unsigned int sigen; + struct super_block *sb; + + sb = d1->d_sb; + err = si_read_lock(sb, flags); + if (unlikely(err)) + goto out; + + di_write_lock2_child(d1, d2, au_ftest_lock(flags, DIRS)); + + if (au_ftest_lock(flags, GEN)) { + sigen = au_sigen(sb); + err = au_digen_test(d1, sigen); + AuDebugOn(!err && au_dbrange_test(d1)); + if (!err) { + err = au_digen_test(d2, sigen); + AuDebugOn(!err && au_dbrange_test(d2)); + } + if (unlikely(err)) + aufs_read_and_write_unlock2(d1, d2); + } + +out: + return err; +} + +void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2) +{ + di_write_unlock2(d1, d2); + si_read_unlock(d1->d_sb); +} diff --git a/fs/aufs/super.c b/fs/aufs/super.c new file mode 100644 index 0000000000000..8406082407ebc --- /dev/null +++ b/fs/aufs/super.c @@ -0,0 +1,868 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * mount and super_block operations + */ + +#include +#include +#include +#include +#include +#include "aufs.h" + +/* + * super_operations + */ +static struct inode *aufs_alloc_inode(struct super_block *sb __maybe_unused) +{ + struct au_icntnr *c; + + c = au_cache_alloc_icntnr(); + if (c) { + au_icntnr_init(c); + inode_set_iversion(&c->vfs_inode, 1); /* sigen(sb); */ + c->iinfo.ii_hinode = NULL; + return &c->vfs_inode; + } + return NULL; +} + +static void aufs_destroy_inode(struct inode *inode) +{ + if (!au_is_bad_inode(inode)) + au_iinfo_fin(inode); +} + +static void aufs_free_inode(struct inode *inode) +{ + au_cache_free_icntnr(container_of(inode, struct au_icntnr, vfs_inode)); +} + +struct inode *au_iget_locked(struct super_block *sb, ino_t ino) +{ + struct inode *inode; + int err; + + inode = iget_locked(sb, ino); + if (unlikely(!inode)) { + inode = ERR_PTR(-ENOMEM); + goto out; + } + if (!(inode->i_state & I_NEW)) + goto out; + + err = au_xigen_new(inode); + if (!err) + err = au_iinfo_init(inode); + if (!err) + inode_inc_iversion(inode); + else { + iget_failed(inode); + inode = ERR_PTR(err); + } + +out: + /* never return NULL */ + AuDebugOn(!inode); + AuTraceErrPtr(inode); + return inode; +} + +/* lock free root dinfo */ +static int au_show_brs(struct seq_file *seq, struct super_block *sb) +{ + int err; + aufs_bindex_t bindex, bbot; + struct path path; + struct au_hdentry *hdp; + struct au_branch *br; + au_br_perm_str_t perm; + + err = 0; + bbot = au_sbbot(sb); + bindex = 0; + hdp = au_hdentry(au_di(sb->s_root), bindex); + for (; !err && bindex <= bbot; bindex++, hdp++) { + br = au_sbr(sb, bindex); + path.mnt = au_br_mnt(br); + path.dentry = hdp->hd_dentry; + err = au_seq_path(seq, &path); + if (!err) { + au_optstr_br_perm(&perm, br->br_perm); + seq_printf(seq, "=%s", perm.a); + if (bindex != bbot) + seq_putc(seq, ':'); + } + } + if (unlikely(err || seq_has_overflowed(seq))) + err = -E2BIG; + + return err; +} + +static void au_gen_fmt(char *fmt, int len __maybe_unused, const char *pat, + const char *append) +{ + char *p; + + p = fmt; + while (*pat != ':') + *p++ = *pat++; + *p++ = *pat++; + strcpy(p, append); + AuDebugOn(strlen(fmt) >= len); +} + +static void au_show_wbr_create(struct seq_file *m, int v, + struct au_sbinfo *sbinfo) +{ + const char *pat; + char fmt[32]; + struct au_wbr_mfs *mfs; + + AuRwMustAnyLock(&sbinfo->si_rwsem); + + seq_puts(m, ",create="); + pat = au_optstr_wbr_create(v); + mfs = &sbinfo->si_wbr_mfs; + switch (v) { + case AuWbrCreate_TDP: + case AuWbrCreate_RR: + case AuWbrCreate_MFS: + case AuWbrCreate_PMFS: + seq_puts(m, pat); + break; + case AuWbrCreate_MFSRR: + case AuWbrCreate_TDMFS: + case AuWbrCreate_PMFSRR: + au_gen_fmt(fmt, sizeof(fmt), pat, "%llu"); + seq_printf(m, fmt, mfs->mfsrr_watermark); + break; + case AuWbrCreate_MFSV: + case AuWbrCreate_PMFSV: + au_gen_fmt(fmt, sizeof(fmt), pat, "%lu"); + seq_printf(m, fmt, + jiffies_to_msecs(mfs->mfs_expire) + / MSEC_PER_SEC); + break; + case AuWbrCreate_MFSRRV: + case AuWbrCreate_TDMFSV: + case AuWbrCreate_PMFSRRV: + au_gen_fmt(fmt, sizeof(fmt), pat, "%llu:%lu"); + seq_printf(m, fmt, mfs->mfsrr_watermark, + jiffies_to_msecs(mfs->mfs_expire) / MSEC_PER_SEC); + break; + default: + BUG(); + } +} + +static int au_show_xino(struct seq_file *seq, struct super_block *sb) +{ +#ifdef CONFIG_SYSFS + return 0; +#else + int err; + const int len = sizeof(AUFS_XINO_FNAME) - 1; + aufs_bindex_t bindex, brid; + struct qstr *name; + struct file *f; + struct dentry *d, *h_root; + struct au_branch *br; + + AuRwMustAnyLock(&sbinfo->si_rwsem); + + err = 0; + f = au_sbi(sb)->si_xib; + if (!f) + goto out; + + /* stop printing the default xino path on the first writable branch */ + h_root = NULL; + bindex = au_xi_root(sb, f->f_path.dentry); + if (bindex >= 0) { + br = au_sbr_sb(sb, bindex); + h_root = au_br_dentry(br); + } + + d = f->f_path.dentry; + name = &d->d_name; + /* safe ->d_parent because the file is unlinked */ + if (d->d_parent == h_root + && name->len == len + && !memcmp(name->name, AUFS_XINO_FNAME, len)) + goto out; + + seq_puts(seq, ",xino="); + err = au_xino_path(seq, f); + +out: + return err; +#endif +} + +/* seq_file will re-call me in case of too long string */ +static int aufs_show_options(struct seq_file *m, struct dentry *dentry) +{ + int err; + unsigned int mnt_flags, v; + struct super_block *sb; + struct au_sbinfo *sbinfo; + +#define AuBool(name, str) do { \ + v = au_opt_test(mnt_flags, name); \ + if (v != au_opt_test(AuOpt_Def, name)) \ + seq_printf(m, ",%s" #str, v ? "" : "no"); \ +} while (0) + +#define AuStr(name, str) do { \ + v = mnt_flags & AuOptMask_##name; \ + if (v != (AuOpt_Def & AuOptMask_##name)) \ + seq_printf(m, "," #str "=%s", au_optstr_##str(v)); \ +} while (0) + +#define AuUInt(name, str, val) do { \ + if (val != AUFS_##name##_DEF) \ + seq_printf(m, "," #str "=%u", val); \ +} while (0) + + sb = dentry->d_sb; + if (sb->s_flags & SB_POSIXACL) + seq_puts(m, ",acl"); +#if 0 /* reserved for future use */ + if (sb->s_flags & SB_I_VERSION) + seq_puts(m, ",i_version"); +#endif + + /* lock free root dinfo */ + si_noflush_read_lock(sb); + sbinfo = au_sbi(sb); + seq_printf(m, ",si=%lx", sysaufs_si_id(sbinfo)); + + mnt_flags = au_mntflags(sb); + if (au_opt_test(mnt_flags, XINO)) { + err = au_show_xino(m, sb); + if (unlikely(err)) + goto out; + } else + seq_puts(m, ",noxino"); + + AuBool(TRUNC_XINO, trunc_xino); + AuStr(UDBA, udba); + AuBool(SHWH, shwh); + AuBool(PLINK, plink); + AuBool(DIO, dio); + AuBool(DIRPERM1, dirperm1); + + v = sbinfo->si_wbr_create; + if (v != AuWbrCreate_Def) + au_show_wbr_create(m, v, sbinfo); + + v = sbinfo->si_wbr_copyup; + if (v != AuWbrCopyup_Def) + seq_printf(m, ",cpup=%s", au_optstr_wbr_copyup(v)); + + v = au_opt_test(mnt_flags, ALWAYS_DIROPQ); + if (v != au_opt_test(AuOpt_Def, ALWAYS_DIROPQ)) + seq_printf(m, ",diropq=%c", v ? 'a' : 'w'); + + AuUInt(DIRWH, dirwh, sbinfo->si_dirwh); + + v = jiffies_to_msecs(sbinfo->si_rdcache) / MSEC_PER_SEC; + AuUInt(RDCACHE, rdcache, v); + + AuUInt(RDBLK, rdblk, sbinfo->si_rdblk); + AuUInt(RDHASH, rdhash, sbinfo->si_rdhash); + + au_fhsm_show(m, sbinfo); + + AuBool(DIRREN, dirren); + AuBool(SUM, sum); + /* AuBool(SUM_W, wsum); */ + AuBool(WARN_PERM, warn_perm); + AuBool(VERBOSE, verbose); + +out: + /* be sure to print "br:" last */ + if (!sysaufs_brs) { + seq_puts(m, ",br:"); + au_show_brs(m, sb); + } + si_read_unlock(sb); + return 0; + +#undef AuBool +#undef AuStr +#undef AuUInt +} + +/* ---------------------------------------------------------------------- */ + +/* sum mode which returns the summation for statfs(2) */ + +static u64 au_add_till_max(u64 a, u64 b) +{ + u64 old; + + old = a; + a += b; + if (old <= a) + return a; + return ULLONG_MAX; +} + +static u64 au_mul_till_max(u64 a, long mul) +{ + u64 old; + + old = a; + a *= mul; + if (old <= a) + return a; + return ULLONG_MAX; +} + +static int au_statfs_sum(struct super_block *sb, struct kstatfs *buf) +{ + int err; + long bsize, factor; + u64 blocks, bfree, bavail, files, ffree; + aufs_bindex_t bbot, bindex, i; + unsigned char shared; + struct path h_path; + struct super_block *h_sb; + + err = 0; + bsize = LONG_MAX; + files = 0; + ffree = 0; + blocks = 0; + bfree = 0; + bavail = 0; + bbot = au_sbbot(sb); + for (bindex = 0; bindex <= bbot; bindex++) { + h_path.mnt = au_sbr_mnt(sb, bindex); + h_sb = h_path.mnt->mnt_sb; + shared = 0; + for (i = 0; !shared && i < bindex; i++) + shared = (au_sbr_sb(sb, i) == h_sb); + if (shared) + continue; + + /* sb->s_root for NFS is unreliable */ + h_path.dentry = h_path.mnt->mnt_root; + err = vfs_statfs(&h_path, buf); + if (unlikely(err)) + goto out; + + if (bsize > buf->f_bsize) { + /* + * we will reduce bsize, so we have to expand blocks + * etc. to match them again + */ + factor = (bsize / buf->f_bsize); + blocks = au_mul_till_max(blocks, factor); + bfree = au_mul_till_max(bfree, factor); + bavail = au_mul_till_max(bavail, factor); + bsize = buf->f_bsize; + } + + factor = (buf->f_bsize / bsize); + blocks = au_add_till_max(blocks, + au_mul_till_max(buf->f_blocks, factor)); + bfree = au_add_till_max(bfree, + au_mul_till_max(buf->f_bfree, factor)); + bavail = au_add_till_max(bavail, + au_mul_till_max(buf->f_bavail, factor)); + files = au_add_till_max(files, buf->f_files); + ffree = au_add_till_max(ffree, buf->f_ffree); + } + + buf->f_bsize = bsize; + buf->f_blocks = blocks; + buf->f_bfree = bfree; + buf->f_bavail = bavail; + buf->f_files = files; + buf->f_ffree = ffree; + buf->f_frsize = 0; + +out: + return err; +} + +static int aufs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + int err; + struct path h_path; + struct super_block *sb; + + /* lock free root dinfo */ + sb = dentry->d_sb; + si_noflush_read_lock(sb); + if (!au_opt_test(au_mntflags(sb), SUM)) { + /* sb->s_root for NFS is unreliable */ + h_path.mnt = au_sbr_mnt(sb, 0); + h_path.dentry = h_path.mnt->mnt_root; + err = vfs_statfs(&h_path, buf); + } else + err = au_statfs_sum(sb, buf); + si_read_unlock(sb); + + if (!err) { + buf->f_type = AUFS_SUPER_MAGIC; + buf->f_namelen = AUFS_MAX_NAMELEN; + memset(&buf->f_fsid, 0, sizeof(buf->f_fsid)); + } + /* buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1; */ + + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int aufs_sync_fs(struct super_block *sb, int wait) +{ + int err, e; + aufs_bindex_t bbot, bindex; + struct au_branch *br; + struct super_block *h_sb; + + err = 0; + si_noflush_read_lock(sb); + bbot = au_sbbot(sb); + for (bindex = 0; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + if (!au_br_writable(br->br_perm)) + continue; + + h_sb = au_sbr_sb(sb, bindex); + e = vfsub_sync_filesystem(h_sb); + if (unlikely(e && !err)) + err = e; + /* go on even if an error happens */ + } + si_read_unlock(sb); + + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* final actions when unmounting a file system */ +static void aufs_put_super(struct super_block *sb) +{ + struct au_sbinfo *sbinfo; + + sbinfo = au_sbi(sb); + if (sbinfo) + kobject_put(&sbinfo->si_kobj); +} + +/* ---------------------------------------------------------------------- */ + +void *au_array_alloc(unsigned long long *hint, au_arraycb_t cb, + struct super_block *sb, void *arg) +{ + void *array; + unsigned long long n, sz; + + array = NULL; + n = 0; + if (!*hint) + goto out; + + if (*hint > ULLONG_MAX / sizeof(array)) { + array = ERR_PTR(-EMFILE); + pr_err("hint %llu\n", *hint); + goto out; + } + + sz = sizeof(array) * *hint; + array = kzalloc(sz, GFP_NOFS); + if (unlikely(!array)) + array = vzalloc(sz); + if (unlikely(!array)) { + array = ERR_PTR(-ENOMEM); + goto out; + } + + n = cb(sb, array, *hint, arg); + AuDebugOn(n > *hint); + +out: + *hint = n; + return array; +} + +static unsigned long long au_iarray_cb(struct super_block *sb, void *a, + unsigned long long max __maybe_unused, + void *arg) +{ + unsigned long long n; + struct inode **p, *inode; + struct list_head *head; + + n = 0; + p = a; + head = arg; + spin_lock(&sb->s_inode_list_lock); + list_for_each_entry(inode, head, i_sb_list) { + if (!au_is_bad_inode(inode) + && au_ii(inode)->ii_btop >= 0) { + spin_lock(&inode->i_lock); + if (atomic_read(&inode->i_count)) { + au_igrab(inode); + *p++ = inode; + n++; + AuDebugOn(n > max); + } + spin_unlock(&inode->i_lock); + } + } + spin_unlock(&sb->s_inode_list_lock); + + return n; +} + +struct inode **au_iarray_alloc(struct super_block *sb, unsigned long long *max) +{ + struct au_sbinfo *sbi; + + sbi = au_sbi(sb); + *max = au_lcnt_read(&sbi->si_ninodes, /*do_rev*/1); + return au_array_alloc(max, au_iarray_cb, sb, &sb->s_inodes); +} + +void au_iarray_free(struct inode **a, unsigned long long max) +{ + unsigned long long ull; + + for (ull = 0; ull < max; ull++) + iput(a[ull]); + kvfree(a); +} + +/* ---------------------------------------------------------------------- */ + +/* + * refresh dentry and inode at remount time. + */ +/* todo: consolidate with simple_reval_dpath() and au_reval_for_attr() */ +static int au_do_refresh(struct dentry *dentry, unsigned int dir_flags, + struct dentry *parent) +{ + int err; + + di_write_lock_child(dentry); + di_read_lock_parent(parent, AuLock_IR); + err = au_refresh_dentry(dentry, parent); + if (!err && dir_flags) + au_hn_reset(d_inode(dentry), dir_flags); + di_read_unlock(parent, AuLock_IR); + di_write_unlock(dentry); + + return err; +} + +static int au_do_refresh_d(struct dentry *dentry, unsigned int sigen, + struct au_sbinfo *sbinfo, + const unsigned int dir_flags, unsigned int do_idop) +{ + int err; + struct dentry *parent; + + err = 0; + parent = dget_parent(dentry); + if (!au_digen_test(parent, sigen) && au_digen_test(dentry, sigen)) { + if (d_really_is_positive(dentry)) { + if (!d_is_dir(dentry)) + err = au_do_refresh(dentry, /*dir_flags*/0, + parent); + else { + err = au_do_refresh(dentry, dir_flags, parent); + if (unlikely(err)) + au_fset_si(sbinfo, FAILED_REFRESH_DIR); + } + } else + err = au_do_refresh(dentry, /*dir_flags*/0, parent); + AuDbgDentry(dentry); + } + dput(parent); + + if (!err) { + if (do_idop) + au_refresh_dop(dentry, /*force_reval*/0); + } else + au_refresh_dop(dentry, /*force_reval*/1); + + AuTraceErr(err); + return err; +} + +static int au_refresh_d(struct super_block *sb, unsigned int do_idop) +{ + int err, i, j, ndentry, e; + unsigned int sigen; + struct au_dcsub_pages dpages; + struct au_dpage *dpage; + struct dentry **dentries, *d; + struct au_sbinfo *sbinfo; + struct dentry *root = sb->s_root; + const unsigned int dir_flags = au_hi_flags(d_inode(root), /*isdir*/1); + + if (do_idop) + au_refresh_dop(root, /*force_reval*/0); + + err = au_dpages_init(&dpages, GFP_NOFS); + if (unlikely(err)) + goto out; + err = au_dcsub_pages(&dpages, root, NULL, NULL); + if (unlikely(err)) + goto out_dpages; + + sigen = au_sigen(sb); + sbinfo = au_sbi(sb); + for (i = 0; i < dpages.ndpage; i++) { + dpage = dpages.dpages + i; + dentries = dpage->dentries; + ndentry = dpage->ndentry; + for (j = 0; j < ndentry; j++) { + d = dentries[j]; + e = au_do_refresh_d(d, sigen, sbinfo, dir_flags, + do_idop); + if (unlikely(e && !err)) + err = e; + /* go on even err */ + } + } + +out_dpages: + au_dpages_free(&dpages); +out: + return err; +} + +static int au_refresh_i(struct super_block *sb, unsigned int do_idop) +{ + int err, e; + unsigned int sigen; + unsigned long long max, ull; + struct inode *inode, **array; + + array = au_iarray_alloc(sb, &max); + err = PTR_ERR(array); + if (IS_ERR(array)) + goto out; + + err = 0; + sigen = au_sigen(sb); + for (ull = 0; ull < max; ull++) { + inode = array[ull]; + if (unlikely(!inode)) + break; + + e = 0; + ii_write_lock_child(inode); + if (au_iigen(inode, NULL) != sigen) { + e = au_refresh_hinode_self(inode); + if (unlikely(e)) { + au_refresh_iop(inode, /*force_getattr*/1); + pr_err("error %d, i%lu\n", e, inode->i_ino); + if (!err) + err = e; + /* go on even if err */ + } + } + if (!e && do_idop) + au_refresh_iop(inode, /*force_getattr*/0); + ii_write_unlock(inode); + } + + au_iarray_free(array, max); + +out: + return err; +} + +void au_remount_refresh(struct super_block *sb, unsigned int do_idop) +{ + int err, e; + unsigned int udba; + aufs_bindex_t bindex, bbot; + struct dentry *root; + struct inode *inode; + struct au_branch *br; + struct au_sbinfo *sbi; + + au_sigen_inc(sb); + sbi = au_sbi(sb); + au_fclr_si(sbi, FAILED_REFRESH_DIR); + + root = sb->s_root; + DiMustNoWaiters(root); + inode = d_inode(root); + IiMustNoWaiters(inode); + + udba = au_opt_udba(sb); + bbot = au_sbbot(sb); + for (bindex = 0; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + err = au_hnotify_reset_br(udba, br, br->br_perm); + if (unlikely(err)) + AuIOErr("hnotify failed on br %d, %d, ignored\n", + bindex, err); + /* go on even if err */ + } + au_hn_reset(inode, au_hi_flags(inode, /*isdir*/1)); + + if (do_idop) { + if (au_ftest_si(sbi, NO_DREVAL)) { + AuDebugOn(sb->s_d_op == &aufs_dop_noreval); + sb->s_d_op = &aufs_dop_noreval; + AuDebugOn(sbi->si_iop_array == aufs_iop_nogetattr); + sbi->si_iop_array = aufs_iop_nogetattr; + } else { + AuDebugOn(sb->s_d_op == &aufs_dop); + sb->s_d_op = &aufs_dop; + AuDebugOn(sbi->si_iop_array == aufs_iop); + sbi->si_iop_array = aufs_iop; + } + pr_info("reset to %ps and %ps\n", + sb->s_d_op, sbi->si_iop_array); + } + + di_write_unlock(root); + err = au_refresh_d(sb, do_idop); + e = au_refresh_i(sb, do_idop); + if (unlikely(e && !err)) + err = e; + /* aufs_write_lock() calls ..._child() */ + di_write_lock_child(root); + + au_cpup_attr_all(inode, /*force*/1); + + if (unlikely(err)) + AuIOErr("refresh failed, ignored, %d\n", err); +} + +const struct super_operations aufs_sop = { + .alloc_inode = aufs_alloc_inode, + .destroy_inode = aufs_destroy_inode, + .free_inode = aufs_free_inode, + /* always deleting, no clearing */ + .drop_inode = generic_delete_inode, + .show_options = aufs_show_options, + .statfs = aufs_statfs, + .put_super = aufs_put_super, + .sync_fs = aufs_sync_fs +}; + +/* ---------------------------------------------------------------------- */ + +int au_alloc_root(struct super_block *sb) +{ + int err; + struct inode *inode; + struct dentry *root; + + err = -ENOMEM; + inode = au_iget_locked(sb, AUFS_ROOT_INO); + err = PTR_ERR(inode); + if (IS_ERR(inode)) + goto out; + + inode->i_op = aufs_iop + AuIop_DIR; /* with getattr by default */ + inode->i_fop = &aufs_dir_fop; + inode->i_mode = S_IFDIR; + set_nlink(inode, 2); + unlock_new_inode(inode); + + root = d_make_root(inode); + if (unlikely(!root)) + goto out; + err = PTR_ERR(root); + if (IS_ERR(root)) + goto out; + + err = au_di_init(root); + if (!err) { + sb->s_root = root; + return 0; /* success */ + } + dput(root); + +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +static void aufs_kill_sb(struct super_block *sb) +{ + struct au_sbinfo *sbinfo; + struct dentry *root; + + sbinfo = au_sbi(sb); + if (!sbinfo) + goto out; + + au_sbilist_del(sb); + + root = sb->s_root; + if (root) + aufs_write_lock(root); + else + __si_write_lock(sb); + + au_fhsm_fin(sb); + if (sbinfo->si_wbr_create_ops->fin) + sbinfo->si_wbr_create_ops->fin(sb); + if (au_opt_test(sbinfo->si_mntflags, UDBA_HNOTIFY)) { + au_opt_set_udba(sbinfo->si_mntflags, UDBA_NONE); + au_remount_refresh(sb, /*do_idop*/0); + } + if (au_opt_test(sbinfo->si_mntflags, PLINK)) + au_plink_put(sb, /*verbose*/1); + au_xino_clr(sb); + if (root) + au_dr_opt_flush(sb); + + if (root) + aufs_write_unlock(root); + else + __si_write_unlock(sb); + + sbinfo->si_sb = NULL; + au_nwt_flush(&sbinfo->si_nowait); + +out: + kill_anon_super(sb); +} + +struct file_system_type aufs_fs_type = { + .name = AUFS_FSTYPE, + /* a race between rename and others */ + .fs_flags = FS_RENAME_DOES_D_MOVE, + .init_fs_context = aufs_fsctx_init, + .parameters = aufs_fsctx_paramspec, + .kill_sb = aufs_kill_sb, + /* no need to __module_get() and module_put(). */ + .owner = THIS_MODULE, +}; diff --git a/fs/aufs/super.h b/fs/aufs/super.h new file mode 100644 index 0000000000000..9f7ff19531850 --- /dev/null +++ b/fs/aufs/super.h @@ -0,0 +1,592 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * super_block operations + */ + +#ifndef __AUFS_SUPER_H__ +#define __AUFS_SUPER_H__ + +#ifdef __KERNEL__ + +#include +#include +#include "hbl.h" +#include "lcnt.h" +#include "rwsem.h" +#include "wkq.h" + +/* policies to select one among multiple writable branches */ +struct au_wbr_copyup_operations { + int (*copyup)(struct dentry *dentry); +}; + +#define AuWbr_DIR 1 /* target is a dir */ +#define AuWbr_PARENT (1 << 1) /* always require a parent */ + +#define au_ftest_wbr(flags, name) ((flags) & AuWbr_##name) +#define au_fset_wbr(flags, name) { (flags) |= AuWbr_##name; } +#define au_fclr_wbr(flags, name) { (flags) &= ~AuWbr_##name; } + +struct au_wbr_create_operations { + int (*create)(struct dentry *dentry, unsigned int flags); + int (*init)(struct super_block *sb); + int (*fin)(struct super_block *sb); +}; + +struct au_wbr_mfs { + struct mutex mfs_lock; /* protect this structure */ + unsigned long mfs_jiffy; + unsigned long mfs_expire; + aufs_bindex_t mfs_bindex; + + unsigned long long mfsrr_bytes; + unsigned long long mfsrr_watermark; +}; + +#define AuPlink_NHASH 100 +static inline int au_plink_hash(ino_t ino) +{ + return ino % AuPlink_NHASH; +} + +/* File-based Hierarchical Storage Management */ +struct au_fhsm { +#ifdef CONFIG_AUFS_FHSM + /* allow only one process who can receive the notification */ + spinlock_t fhsm_spin; + pid_t fhsm_pid; + wait_queue_head_t fhsm_wqh; + atomic_t fhsm_readable; + + /* these are protected by si_rwsem */ + unsigned long fhsm_expire; + aufs_bindex_t fhsm_bottom; +#endif +}; + +struct au_branch; +struct au_sbinfo { + /* nowait tasks in the system-wide workqueue */ + struct au_nowait_tasks si_nowait; + + /* + * tried sb->s_umount, but failed due to the dependency between i_mutex. + * rwsem for au_sbinfo is necessary. + */ + struct au_rwsem si_rwsem; + + /* + * dirty approach to protect sb->sb_inodes and ->s_files (gone) from + * remount. + */ + au_lcnt_t si_ninodes, si_nfiles; + + /* branch management */ + unsigned int si_generation; + + /* see AuSi_ flags */ + unsigned char au_si_status; + + aufs_bindex_t si_bbot; + + /* dirty trick to keep br_id plus */ + unsigned int si_last_br_id : + sizeof(aufs_bindex_t) * BITS_PER_BYTE - 1; + struct au_branch **si_branch; + + /* policy to select a writable branch */ + unsigned char si_wbr_copyup; + unsigned char si_wbr_create; + struct au_wbr_copyup_operations *si_wbr_copyup_ops; + struct au_wbr_create_operations *si_wbr_create_ops; + + /* round robin */ + atomic_t si_wbr_rr_next; + + /* most free space */ + struct au_wbr_mfs si_wbr_mfs; + + /* File-based Hierarchical Storage Management */ + struct au_fhsm si_fhsm; + + /* mount flags */ + /* include/asm-ia64/siginfo.h defines a macro named si_flags */ + unsigned int si_mntflags; + + /* external inode number (bitmap and translation table) */ + loff_t si_ximaxent; /* max entries in a xino */ + + struct file *si_xib; + struct mutex si_xib_mtx; /* protect xib members */ + unsigned long *si_xib_buf; + unsigned long si_xib_last_pindex; + int si_xib_next_bit; + + unsigned long si_xino_jiffy; + unsigned long si_xino_expire; + /* reserved for future use */ + /* unsigned long long si_xib_limit; */ /* Max xib file size */ + +#ifdef CONFIG_AUFS_EXPORT + /* i_generation */ + /* todo: make xigen file an array to support many inode numbers */ + struct file *si_xigen; + atomic_t si_xigen_next; +#endif + + /* dirty trick to support atomic_open */ + struct hlist_bl_head si_aopen; + + /* vdir parameters */ + unsigned long si_rdcache; /* max cache time in jiffies */ + unsigned int si_rdblk; /* deblk size */ + unsigned int si_rdhash; /* hash size */ + + /* + * If the number of whiteouts are larger than si_dirwh, leave all of + * them after au_whtmp_ren to reduce the cost of rmdir(2). + * future fsck.aufs or kernel thread will remove them later. + * Otherwise, remove all whiteouts and the dir in rmdir(2). + */ + unsigned int si_dirwh; + + /* pseudo_link list */ + struct hlist_bl_head si_plink[AuPlink_NHASH]; + wait_queue_head_t si_plink_wq; + spinlock_t si_plink_maint_lock; + pid_t si_plink_maint_pid; + + /* file list */ + struct hlist_bl_head si_files; + + /* with/without getattr, brother of sb->s_d_op */ + const struct inode_operations *si_iop_array; + + /* + * sysfs and lifetime management. + * this is not a small structure and it may be a waste of memory in case + * of sysfs is disabled, particularly when many aufs-es are mounted. + * but using sysfs is majority. + */ + struct kobject si_kobj; +#ifdef CONFIG_DEBUG_FS + struct dentry *si_dbgaufs; + struct dentry *si_dbgaufs_plink; + struct dentry *si_dbgaufs_xib; +#ifdef CONFIG_AUFS_EXPORT + struct dentry *si_dbgaufs_xigen; +#endif +#endif + +#ifdef CONFIG_AUFS_SBILIST + struct hlist_bl_node si_list; +#endif + + /* dirty, necessary for unmounting, sysfs and sysrq */ + struct super_block *si_sb; +}; + +/* sbinfo status flags */ +/* + * set true when refresh_dirs() failed at remount time. + * then try refreshing dirs at access time again. + * if it is false, refreshing dirs at access time is unnecessary + */ +#define AuSi_FAILED_REFRESH_DIR 1 +#define AuSi_FHSM (1 << 1) /* fhsm is active now */ +#define AuSi_NO_DREVAL (1 << 2) /* disable all d_revalidate */ + +#ifndef CONFIG_AUFS_FHSM +#undef AuSi_FHSM +#define AuSi_FHSM 0 +#endif + +static inline unsigned char au_do_ftest_si(struct au_sbinfo *sbi, + unsigned int flag) +{ + AuRwMustAnyLock(&sbi->si_rwsem); + return sbi->au_si_status & flag; +} +#define au_ftest_si(sbinfo, name) au_do_ftest_si(sbinfo, AuSi_##name) +#define au_fset_si(sbinfo, name) do { \ + AuRwMustWriteLock(&(sbinfo)->si_rwsem); \ + (sbinfo)->au_si_status |= AuSi_##name; \ +} while (0) +#define au_fclr_si(sbinfo, name) do { \ + AuRwMustWriteLock(&(sbinfo)->si_rwsem); \ + (sbinfo)->au_si_status &= ~AuSi_##name; \ +} while (0) + +/* ---------------------------------------------------------------------- */ + +/* policy to select one among writable branches */ +#define AuWbrCopyup(sbinfo, ...) \ + ((sbinfo)->si_wbr_copyup_ops->copyup(__VA_ARGS__)) +#define AuWbrCreate(sbinfo, ...) \ + ((sbinfo)->si_wbr_create_ops->create(__VA_ARGS__)) + +/* flags for si_read_lock()/aufs_read_lock()/di_read_lock() */ +#define AuLock_DW 1 /* write-lock dentry */ +#define AuLock_IR (1 << 1) /* read-lock inode */ +#define AuLock_IW (1 << 2) /* write-lock inode */ +#define AuLock_FLUSH (1 << 3) /* wait for 'nowait' tasks */ +#define AuLock_DIRS (1 << 4) /* target is a pair of dirs */ + /* except RENAME_EXCHANGE */ +#define AuLock_NOPLM (1 << 5) /* return err in plm mode */ +#define AuLock_NOPLMW (1 << 6) /* wait for plm mode ends */ +#define AuLock_GEN (1 << 7) /* test digen/iigen */ +#define au_ftest_lock(flags, name) ((flags) & AuLock_##name) +#define au_fset_lock(flags, name) \ + do { (flags) |= AuLock_##name; } while (0) +#define au_fclr_lock(flags, name) \ + do { (flags) &= ~AuLock_##name; } while (0) + +/* ---------------------------------------------------------------------- */ + +/* super.c */ +struct inode *au_iget_locked(struct super_block *sb, ino_t ino); + +typedef unsigned long long (*au_arraycb_t)(struct super_block *sb, void *array, + unsigned long long max, void *arg); +void *au_array_alloc(unsigned long long *hint, au_arraycb_t cb, + struct super_block *sb, void *arg); +struct inode **au_iarray_alloc(struct super_block *sb, unsigned long long *max); +void au_iarray_free(struct inode **a, unsigned long long max); + +void au_remount_refresh(struct super_block *sb, unsigned int do_idop); +extern const struct super_operations aufs_sop; +int au_alloc_root(struct super_block *sb); +extern struct file_system_type aufs_fs_type; + +/* sbinfo.c */ +void au_si_free(struct kobject *kobj); +struct au_sbinfo *au_si_alloc(struct super_block *sb); +int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr, int may_shrink); + +unsigned int au_sigen_inc(struct super_block *sb); +aufs_bindex_t au_new_br_id(struct super_block *sb); + +int si_read_lock(struct super_block *sb, int flags); +int si_write_lock(struct super_block *sb, int flags); +int aufs_read_lock(struct dentry *dentry, int flags); +void aufs_read_unlock(struct dentry *dentry, int flags); +void aufs_write_lock(struct dentry *dentry); +void aufs_write_unlock(struct dentry *dentry); +int aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags); +void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2); + +/* wbr_policy.c */ +extern struct au_wbr_copyup_operations au_wbr_copyup_ops[]; +extern struct au_wbr_create_operations au_wbr_create_ops[]; +int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst); +int au_wbr_nonopq(struct dentry *dentry, aufs_bindex_t bindex); +int au_wbr_do_copyup_bu(struct dentry *dentry, aufs_bindex_t btop); + +/* mvdown.c */ +int au_mvdown(struct dentry *dentry, struct aufs_mvdown __user *arg); + +#ifdef CONFIG_AUFS_FHSM +/* fhsm.c */ + +static inline pid_t au_fhsm_pid(struct au_fhsm *fhsm) +{ + pid_t pid; + + spin_lock(&fhsm->fhsm_spin); + pid = fhsm->fhsm_pid; + spin_unlock(&fhsm->fhsm_spin); + + return pid; +} + +void au_fhsm_wrote(struct super_block *sb, aufs_bindex_t bindex, int force); +void au_fhsm_wrote_all(struct super_block *sb, int force); +int au_fhsm_fd(struct super_block *sb, int oflags); +int au_fhsm_br_alloc(struct au_branch *br); +void au_fhsm_set_bottom(struct super_block *sb, aufs_bindex_t bindex); +void au_fhsm_fin(struct super_block *sb); +void au_fhsm_init(struct au_sbinfo *sbinfo); +void au_fhsm_set(struct au_sbinfo *sbinfo, unsigned int sec); +void au_fhsm_show(struct seq_file *seq, struct au_sbinfo *sbinfo); +#else +AuStubVoid(au_fhsm_wrote, struct super_block *sb, aufs_bindex_t bindex, + int force) +AuStubVoid(au_fhsm_wrote_all, struct super_block *sb, int force) +AuStub(int, au_fhsm_fd, return -EOPNOTSUPP, struct super_block *sb, int oflags) +AuStub(pid_t, au_fhsm_pid, return 0, struct au_fhsm *fhsm) +AuStubInt0(au_fhsm_br_alloc, struct au_branch *br) +AuStubVoid(au_fhsm_set_bottom, struct super_block *sb, aufs_bindex_t bindex) +AuStubVoid(au_fhsm_fin, struct super_block *sb) +AuStubVoid(au_fhsm_init, struct au_sbinfo *sbinfo) +AuStubVoid(au_fhsm_set, struct au_sbinfo *sbinfo, unsigned int sec) +AuStubVoid(au_fhsm_show, struct seq_file *seq, struct au_sbinfo *sbinfo) +#endif + +/* ---------------------------------------------------------------------- */ + +static inline struct au_sbinfo *au_sbi(struct super_block *sb) +{ + return sb->s_fs_info; +} + +/* ---------------------------------------------------------------------- */ + +#ifdef CONFIG_AUFS_EXPORT +int au_test_nfsd(void); +void au_export_init(struct super_block *sb); +void au_xigen_inc(struct inode *inode); +int au_xigen_new(struct inode *inode); +int au_xigen_set(struct super_block *sb, struct path *path); +void au_xigen_clr(struct super_block *sb); + +static inline int au_busy_or_stale(void) +{ + if (!au_test_nfsd()) + return -EBUSY; + return -ESTALE; +} +#else +AuStubInt0(au_test_nfsd, void) +AuStubVoid(au_export_init, struct super_block *sb) +AuStubVoid(au_xigen_inc, struct inode *inode) +AuStubInt0(au_xigen_new, struct inode *inode) +AuStubInt0(au_xigen_set, struct super_block *sb, struct path *path) +AuStubVoid(au_xigen_clr, struct super_block *sb) +AuStub(int, au_busy_or_stale, return -EBUSY, void) +#endif /* CONFIG_AUFS_EXPORT */ + +/* ---------------------------------------------------------------------- */ + +#ifdef CONFIG_AUFS_SBILIST +/* module.c */ +extern struct hlist_bl_head au_sbilist; + +static inline void au_sbilist_init(void) +{ + INIT_HLIST_BL_HEAD(&au_sbilist); +} + +static inline void au_sbilist_add(struct super_block *sb) +{ + au_hbl_add(&au_sbi(sb)->si_list, &au_sbilist); +} + +static inline void au_sbilist_del(struct super_block *sb) +{ + au_hbl_del(&au_sbi(sb)->si_list, &au_sbilist); +} + +#ifdef CONFIG_AUFS_MAGIC_SYSRQ +static inline void au_sbilist_lock(void) +{ + hlist_bl_lock(&au_sbilist); +} + +static inline void au_sbilist_unlock(void) +{ + hlist_bl_unlock(&au_sbilist); +} +#define AuGFP_SBILIST GFP_ATOMIC +#else +AuStubVoid(au_sbilist_lock, void) +AuStubVoid(au_sbilist_unlock, void) +#define AuGFP_SBILIST GFP_NOFS +#endif /* CONFIG_AUFS_MAGIC_SYSRQ */ +#else +AuStubVoid(au_sbilist_init, void) +AuStubVoid(au_sbilist_add, struct super_block *sb) +AuStubVoid(au_sbilist_del, struct super_block *sb) +AuStubVoid(au_sbilist_lock, void) +AuStubVoid(au_sbilist_unlock, void) +#define AuGFP_SBILIST GFP_NOFS +#endif + +/* ---------------------------------------------------------------------- */ + +static inline void dbgaufs_si_null(struct au_sbinfo *sbinfo) +{ + /* + * This function is a dynamic '__init' function actually, + * so the tiny check for si_rwsem is unnecessary. + */ + /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ +#ifdef CONFIG_DEBUG_FS + sbinfo->si_dbgaufs = NULL; + sbinfo->si_dbgaufs_plink = NULL; + sbinfo->si_dbgaufs_xib = NULL; +#ifdef CONFIG_AUFS_EXPORT + sbinfo->si_dbgaufs_xigen = NULL; +#endif +#endif +} + +/* ---------------------------------------------------------------------- */ + +/* current->atomic_flags */ +/* this value should never corrupt the ones defined in linux/sched.h */ +#define PFA_AUFS 0x10 + +TASK_PFA_TEST(AUFS, test_aufs) /* task_test_aufs */ +TASK_PFA_SET(AUFS, aufs) /* task_set_aufs */ +TASK_PFA_CLEAR(AUFS, aufs) /* task_clear_aufs */ + +static inline int si_pid_test(struct super_block *sb) +{ + return !!task_test_aufs(current); +} + +static inline void si_pid_clr(struct super_block *sb) +{ + AuDebugOn(!task_test_aufs(current)); + task_clear_aufs(current); +} + +static inline void si_pid_set(struct super_block *sb) +{ + AuDebugOn(task_test_aufs(current)); + task_set_aufs(current); +} + +/* ---------------------------------------------------------------------- */ + +/* lock superblock. mainly for entry point functions */ +#define __si_read_lock(sb) au_rw_read_lock(&au_sbi(sb)->si_rwsem) +#define __si_write_lock(sb) au_rw_write_lock(&au_sbi(sb)->si_rwsem) +#define __si_read_trylock(sb) au_rw_read_trylock(&au_sbi(sb)->si_rwsem) +#define __si_write_trylock(sb) au_rw_write_trylock(&au_sbi(sb)->si_rwsem) +/* +#define __si_read_trylock_nested(sb) \ + au_rw_read_trylock_nested(&au_sbi(sb)->si_rwsem) +#define __si_write_trylock_nested(sb) \ + au_rw_write_trylock_nested(&au_sbi(sb)->si_rwsem) +*/ + +#define __si_read_unlock(sb) au_rw_read_unlock(&au_sbi(sb)->si_rwsem) +#define __si_write_unlock(sb) au_rw_write_unlock(&au_sbi(sb)->si_rwsem) +#define __si_downgrade_lock(sb) au_rw_dgrade_lock(&au_sbi(sb)->si_rwsem) + +#define SiMustNoWaiters(sb) AuRwMustNoWaiters(&au_sbi(sb)->si_rwsem) +#define SiMustAnyLock(sb) AuRwMustAnyLock(&au_sbi(sb)->si_rwsem) +#define SiMustWriteLock(sb) AuRwMustWriteLock(&au_sbi(sb)->si_rwsem) + +static inline void si_noflush_read_lock(struct super_block *sb) +{ + __si_read_lock(sb); + si_pid_set(sb); +} + +static inline int si_noflush_read_trylock(struct super_block *sb) +{ + int locked; + + locked = __si_read_trylock(sb); + if (locked) + si_pid_set(sb); + return locked; +} + +static inline void si_noflush_write_lock(struct super_block *sb) +{ + __si_write_lock(sb); + si_pid_set(sb); +} + +static inline int si_noflush_write_trylock(struct super_block *sb) +{ + int locked; + + locked = __si_write_trylock(sb); + if (locked) + si_pid_set(sb); + return locked; +} + +#if 0 /* reserved */ +static inline int si_read_trylock(struct super_block *sb, int flags) +{ + if (au_ftest_lock(flags, FLUSH)) + au_nwt_flush(&au_sbi(sb)->si_nowait); + return si_noflush_read_trylock(sb); +} +#endif + +static inline void si_read_unlock(struct super_block *sb) +{ + si_pid_clr(sb); + __si_read_unlock(sb); +} + +#if 0 /* reserved */ +static inline int si_write_trylock(struct super_block *sb, int flags) +{ + if (au_ftest_lock(flags, FLUSH)) + au_nwt_flush(&au_sbi(sb)->si_nowait); + return si_noflush_write_trylock(sb); +} +#endif + +static inline void si_write_unlock(struct super_block *sb) +{ + si_pid_clr(sb); + __si_write_unlock(sb); +} + +#if 0 /* reserved */ +static inline void si_downgrade_lock(struct super_block *sb) +{ + __si_downgrade_lock(sb); +} +#endif + +/* ---------------------------------------------------------------------- */ + +static inline aufs_bindex_t au_sbbot(struct super_block *sb) +{ + SiMustAnyLock(sb); + return au_sbi(sb)->si_bbot; +} + +static inline unsigned int au_mntflags(struct super_block *sb) +{ + SiMustAnyLock(sb); + return au_sbi(sb)->si_mntflags; +} + +static inline unsigned int au_sigen(struct super_block *sb) +{ + SiMustAnyLock(sb); + return au_sbi(sb)->si_generation; +} + +static inline struct au_branch *au_sbr(struct super_block *sb, + aufs_bindex_t bindex) +{ + SiMustAnyLock(sb); + return au_sbi(sb)->si_branch[0 + bindex]; +} + +static inline loff_t au_xi_maxent(struct super_block *sb) +{ + SiMustAnyLock(sb); + return au_sbi(sb)->si_ximaxent; +} + +#endif /* __KERNEL__ */ +#endif /* __AUFS_SUPER_H__ */ diff --git a/fs/aufs/sysaufs.c b/fs/aufs/sysaufs.c new file mode 100644 index 0000000000000..00d53dea4bf9e --- /dev/null +++ b/fs/aufs/sysaufs.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * sysfs interface and lifetime management + * they are necessary regardless sysfs is disabled. + */ + +#include +#include "aufs.h" + +unsigned long sysaufs_si_mask; +struct kset *sysaufs_kset; + +#define AuSiAttr(_name) { \ + .attr = { .name = __stringify(_name), .mode = 0444 }, \ + .show = sysaufs_si_##_name, \ +} + +static struct sysaufs_si_attr sysaufs_si_attr_xi_path = AuSiAttr(xi_path); +struct attribute *sysaufs_si_attrs[] = { + &sysaufs_si_attr_xi_path.attr, + NULL, +}; + +static const struct sysfs_ops au_sbi_ops = { + .show = sysaufs_si_show +}; + +static struct kobj_type au_sbi_ktype = { + .release = au_si_free, + .sysfs_ops = &au_sbi_ops, + .default_attrs = sysaufs_si_attrs +}; + +/* ---------------------------------------------------------------------- */ + +int sysaufs_si_init(struct au_sbinfo *sbinfo) +{ + int err; + + sbinfo->si_kobj.kset = sysaufs_kset; + /* cf. sysaufs_name() */ + err = kobject_init_and_add + (&sbinfo->si_kobj, &au_sbi_ktype, /*&sysaufs_kset->kobj*/NULL, + SysaufsSiNamePrefix "%lx", sysaufs_si_id(sbinfo)); + + return err; +} + +void sysaufs_fin(void) +{ + sysfs_remove_group(&sysaufs_kset->kobj, sysaufs_attr_group); + kset_unregister(sysaufs_kset); +} + +int __init sysaufs_init(void) +{ + int err; + + do { + get_random_bytes(&sysaufs_si_mask, sizeof(sysaufs_si_mask)); + } while (!sysaufs_si_mask); + + err = -EINVAL; + sysaufs_kset = kset_create_and_add(AUFS_NAME, NULL, fs_kobj); + if (unlikely(!sysaufs_kset)) + goto out; + err = PTR_ERR(sysaufs_kset); + if (IS_ERR(sysaufs_kset)) + goto out; + err = sysfs_create_group(&sysaufs_kset->kobj, sysaufs_attr_group); + if (unlikely(err)) + kset_unregister(sysaufs_kset); + +out: + return err; +} diff --git a/fs/aufs/sysaufs.h b/fs/aufs/sysaufs.h new file mode 100644 index 0000000000000..32b5c2cb6a7d3 --- /dev/null +++ b/fs/aufs/sysaufs.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * sysfs interface and mount lifetime management + */ + +#ifndef __SYSAUFS_H__ +#define __SYSAUFS_H__ + +#ifdef __KERNEL__ + +#include +#include "module.h" + +struct super_block; +struct au_sbinfo; + +struct sysaufs_si_attr { + struct attribute attr; + int (*show)(struct seq_file *seq, struct super_block *sb); +}; + +/* ---------------------------------------------------------------------- */ + +/* sysaufs.c */ +extern unsigned long sysaufs_si_mask; +extern struct kset *sysaufs_kset; +extern struct attribute *sysaufs_si_attrs[]; +int sysaufs_si_init(struct au_sbinfo *sbinfo); +int __init sysaufs_init(void); +void sysaufs_fin(void); + +/* ---------------------------------------------------------------------- */ + +/* some people doesn't like to show a pointer in kernel */ +static inline unsigned long sysaufs_si_id(struct au_sbinfo *sbinfo) +{ + return sysaufs_si_mask ^ (unsigned long)sbinfo; +} + +#define SysaufsSiNamePrefix "si_" +#define SysaufsSiNameLen (sizeof(SysaufsSiNamePrefix) + 16) +static inline void sysaufs_name(struct au_sbinfo *sbinfo, char *name) +{ + snprintf(name, SysaufsSiNameLen, SysaufsSiNamePrefix "%lx", + sysaufs_si_id(sbinfo)); +} + +struct au_branch; +#ifdef CONFIG_SYSFS +/* sysfs.c */ +extern struct attribute_group *sysaufs_attr_group; + +int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb); +ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr, + char *buf); +long au_brinfo_ioctl(struct file *file, unsigned long arg); +#ifdef CONFIG_COMPAT +long au_brinfo_compat_ioctl(struct file *file, unsigned long arg); +#endif + +void sysaufs_br_init(struct au_branch *br); +void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex); +void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex); + +#define sysaufs_brs_init() do {} while (0) + +#else +#define sysaufs_attr_group NULL + +AuStubInt0(sysaufs_si_xi_path, struct seq_file *seq, struct super_block *sb) +AuStub(ssize_t, sysaufs_si_show, return 0, struct kobject *kobj, + struct attribute *attr, char *buf) +AuStubVoid(sysaufs_br_init, struct au_branch *br) +AuStubVoid(sysaufs_brs_add, struct super_block *sb, aufs_bindex_t bindex) +AuStubVoid(sysaufs_brs_del, struct super_block *sb, aufs_bindex_t bindex) + +static inline void sysaufs_brs_init(void) +{ + sysaufs_brs = 0; +} + +#endif /* CONFIG_SYSFS */ + +#endif /* __KERNEL__ */ +#endif /* __SYSAUFS_H__ */ diff --git a/fs/aufs/sysfs.c b/fs/aufs/sysfs.c new file mode 100644 index 0000000000000..519eb1fc6a7ae --- /dev/null +++ b/fs/aufs/sysfs.c @@ -0,0 +1,374 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * sysfs interface + */ + +#include +#include +#include "aufs.h" + +#ifdef CONFIG_AUFS_FS_MODULE +/* this entry violates the "one line per file" policy of sysfs */ +static ssize_t config_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + ssize_t err; + static char *conf = +/* this file is generated at compiling */ +#include "conf.str" + ; + + err = snprintf(buf, PAGE_SIZE, conf); + if (unlikely(err >= PAGE_SIZE)) + err = -EFBIG; + return err; +} + +static struct kobj_attribute au_config_attr = __ATTR_RO(config); +#endif + +static struct attribute *au_attr[] = { +#ifdef CONFIG_AUFS_FS_MODULE + &au_config_attr.attr, +#endif + NULL, /* need to NULL terminate the list of attributes */ +}; + +static struct attribute_group sysaufs_attr_group_body = { + .attrs = au_attr +}; + +struct attribute_group *sysaufs_attr_group = &sysaufs_attr_group_body; + +/* ---------------------------------------------------------------------- */ + +int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb) +{ + int err; + + SiMustAnyLock(sb); + + err = 0; + if (au_opt_test(au_mntflags(sb), XINO)) { + err = au_xino_path(seq, au_sbi(sb)->si_xib); + seq_putc(seq, '\n'); + } + return err; +} + +/* + * the lifetime of branch is independent from the entry under sysfs. + * sysfs handles the lifetime of the entry, and never call ->show() after it is + * unlinked. + */ +static int sysaufs_si_br(struct seq_file *seq, struct super_block *sb, + aufs_bindex_t bindex, int idx) +{ + int err; + struct path path; + struct dentry *root; + struct au_branch *br; + au_br_perm_str_t perm; + + AuDbg("b%d\n", bindex); + + err = 0; + root = sb->s_root; + di_read_lock_parent(root, !AuLock_IR); + br = au_sbr(sb, bindex); + + switch (idx) { + case AuBrSysfs_BR: + path.mnt = au_br_mnt(br); + path.dentry = au_h_dptr(root, bindex); + err = au_seq_path(seq, &path); + if (!err) { + au_optstr_br_perm(&perm, br->br_perm); + seq_printf(seq, "=%s\n", perm.a); + } + break; + case AuBrSysfs_BRID: + seq_printf(seq, "%d\n", br->br_id); + break; + } + di_read_unlock(root, !AuLock_IR); + if (unlikely(err || seq_has_overflowed(seq))) + err = -E2BIG; + + return err; +} + +/* ---------------------------------------------------------------------- */ + +static struct seq_file *au_seq(char *p, ssize_t len) +{ + struct seq_file *seq; + + seq = kzalloc(sizeof(*seq), GFP_NOFS); + if (seq) { + /* mutex_init(&seq.lock); */ + seq->buf = p; + seq->size = len; + return seq; /* success */ + } + + seq = ERR_PTR(-ENOMEM); + return seq; +} + +#define SysaufsBr_PREFIX "br" +#define SysaufsBrid_PREFIX "brid" + +/* todo: file size may exceed PAGE_SIZE */ +ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + ssize_t err; + int idx; + long l; + aufs_bindex_t bbot; + struct au_sbinfo *sbinfo; + struct super_block *sb; + struct seq_file *seq; + char *name; + struct attribute **cattr; + + sbinfo = container_of(kobj, struct au_sbinfo, si_kobj); + sb = sbinfo->si_sb; + + /* + * prevent a race condition between sysfs and aufs. + * for instance, sysfs_file_read() calls sysfs_get_active_two() which + * prohibits maintaining the sysfs entries. + * hew we acquire read lock after sysfs_get_active_two(). + * on the other hand, the remount process may maintain the sysfs/aufs + * entries after acquiring write lock. + * it can cause a deadlock. + * simply we gave up processing read here. + */ + err = -EBUSY; + if (unlikely(!si_noflush_read_trylock(sb))) + goto out; + + seq = au_seq(buf, PAGE_SIZE); + err = PTR_ERR(seq); + if (IS_ERR(seq)) + goto out_unlock; + + name = (void *)attr->name; + cattr = sysaufs_si_attrs; + while (*cattr) { + if (!strcmp(name, (*cattr)->name)) { + err = container_of(*cattr, struct sysaufs_si_attr, attr) + ->show(seq, sb); + goto out_seq; + } + cattr++; + } + + if (!strncmp(name, SysaufsBrid_PREFIX, + sizeof(SysaufsBrid_PREFIX) - 1)) { + idx = AuBrSysfs_BRID; + name += sizeof(SysaufsBrid_PREFIX) - 1; + } else if (!strncmp(name, SysaufsBr_PREFIX, + sizeof(SysaufsBr_PREFIX) - 1)) { + idx = AuBrSysfs_BR; + name += sizeof(SysaufsBr_PREFIX) - 1; + } else + BUG(); + + err = kstrtol(name, 10, &l); + if (!err) { + bbot = au_sbbot(sb); + if (l <= bbot) + err = sysaufs_si_br(seq, sb, (aufs_bindex_t)l, idx); + else + err = -ENOENT; + } + +out_seq: + if (!err) { + err = seq->count; + /* sysfs limit */ + if (unlikely(err == PAGE_SIZE)) + err = -EFBIG; + } + au_kfree_rcu(seq); +out_unlock: + si_read_unlock(sb); +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int au_brinfo(struct super_block *sb, union aufs_brinfo __user *arg) +{ + int err; + int16_t brid; + aufs_bindex_t bindex, bbot; + size_t sz; + char *buf; + struct seq_file *seq; + struct au_branch *br; + + si_read_lock(sb, AuLock_FLUSH); + bbot = au_sbbot(sb); + err = bbot + 1; + if (!arg) + goto out; + + err = -ENOMEM; + buf = (void *)__get_free_page(GFP_NOFS); + if (unlikely(!buf)) + goto out; + + seq = au_seq(buf, PAGE_SIZE); + err = PTR_ERR(seq); + if (IS_ERR(seq)) + goto out_buf; + + sz = sizeof(*arg) - offsetof(union aufs_brinfo, path); + for (bindex = 0; bindex <= bbot; bindex++, arg++) { + /* VERIFY_WRITE */ + err = !access_ok(arg, sizeof(*arg)); + if (unlikely(err)) + break; + + br = au_sbr(sb, bindex); + brid = br->br_id; + BUILD_BUG_ON(sizeof(brid) != sizeof(arg->id)); + err = __put_user(brid, &arg->id); + if (unlikely(err)) + break; + + BUILD_BUG_ON(sizeof(br->br_perm) != sizeof(arg->perm)); + err = __put_user(br->br_perm, &arg->perm); + if (unlikely(err)) + break; + + err = au_seq_path(seq, &br->br_path); + if (unlikely(err)) + break; + seq_putc(seq, '\0'); + if (!seq_has_overflowed(seq)) { + err = copy_to_user(arg->path, seq->buf, seq->count); + seq->count = 0; + if (unlikely(err)) + break; + } else { + err = -E2BIG; + goto out_seq; + } + } + if (unlikely(err)) + err = -EFAULT; + +out_seq: + au_kfree_rcu(seq); +out_buf: + free_page((unsigned long)buf); +out: + si_read_unlock(sb); + return err; +} + +long au_brinfo_ioctl(struct file *file, unsigned long arg) +{ + return au_brinfo(file->f_path.dentry->d_sb, (void __user *)arg); +} + +#ifdef CONFIG_COMPAT +long au_brinfo_compat_ioctl(struct file *file, unsigned long arg) +{ + return au_brinfo(file->f_path.dentry->d_sb, compat_ptr(arg)); +} +#endif + +/* ---------------------------------------------------------------------- */ + +void sysaufs_br_init(struct au_branch *br) +{ + int i; + struct au_brsysfs *br_sysfs; + struct attribute *attr; + + br_sysfs = br->br_sysfs; + for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { + attr = &br_sysfs->attr; + sysfs_attr_init(attr); + attr->name = br_sysfs->name; + attr->mode = 0444; + br_sysfs++; + } +} + +void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) +{ + struct au_branch *br; + struct kobject *kobj; + struct au_brsysfs *br_sysfs; + int i; + aufs_bindex_t bbot; + + if (!sysaufs_brs) + return; + + kobj = &au_sbi(sb)->si_kobj; + bbot = au_sbbot(sb); + for (; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + br_sysfs = br->br_sysfs; + for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { + sysfs_remove_file(kobj, &br_sysfs->attr); + br_sysfs++; + } + } +} + +void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) +{ + int err, i; + aufs_bindex_t bbot; + struct kobject *kobj; + struct au_branch *br; + struct au_brsysfs *br_sysfs; + + if (!sysaufs_brs) + return; + + kobj = &au_sbi(sb)->si_kobj; + bbot = au_sbbot(sb); + for (; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + br_sysfs = br->br_sysfs; + snprintf(br_sysfs[AuBrSysfs_BR].name, sizeof(br_sysfs->name), + SysaufsBr_PREFIX "%d", bindex); + snprintf(br_sysfs[AuBrSysfs_BRID].name, sizeof(br_sysfs->name), + SysaufsBrid_PREFIX "%d", bindex); + for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { + err = sysfs_create_file(kobj, &br_sysfs->attr); + if (unlikely(err)) + pr_warn("failed %s under sysfs(%d)\n", + br_sysfs->name, err); + br_sysfs++; + } + } +} diff --git a/fs/aufs/sysrq.c b/fs/aufs/sysrq.c new file mode 100644 index 0000000000000..b801bf162d268 --- /dev/null +++ b/fs/aufs/sysrq.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * magic sysrq handler + */ + +/* #include */ +#include +#include "aufs.h" + +/* ---------------------------------------------------------------------- */ + +static void sysrq_sb(struct super_block *sb) +{ + char *plevel; + struct au_sbinfo *sbinfo; + struct file *file; + struct hlist_bl_head *files; + struct hlist_bl_node *pos; + struct au_finfo *finfo; + struct inode *i; + + plevel = au_plevel; + au_plevel = KERN_WARNING; + + /* since we define pr_fmt, call printk directly */ +#define pr(str) printk(KERN_WARNING AUFS_NAME ": " str) + + sbinfo = au_sbi(sb); + printk(KERN_WARNING "si=%lx\n", sysaufs_si_id(sbinfo)); + pr("superblock\n"); + au_dpri_sb(sb); + +#if 0 /* reserved */ + do { + int err, i, j, ndentry; + struct au_dcsub_pages dpages; + struct au_dpage *dpage; + + err = au_dpages_init(&dpages, GFP_ATOMIC); + if (unlikely(err)) + break; + err = au_dcsub_pages(&dpages, sb->s_root, NULL, NULL); + if (!err) + for (i = 0; i < dpages.ndpage; i++) { + dpage = dpages.dpages + i; + ndentry = dpage->ndentry; + for (j = 0; j < ndentry; j++) + au_dpri_dentry(dpage->dentries[j]); + } + au_dpages_free(&dpages); + } while (0); +#endif + + pr("isolated inode\n"); + spin_lock(&sb->s_inode_list_lock); + list_for_each_entry(i, &sb->s_inodes, i_sb_list) { + spin_lock(&i->i_lock); + if (hlist_empty(&i->i_dentry)) + au_dpri_inode(i); + spin_unlock(&i->i_lock); + } + spin_unlock(&sb->s_inode_list_lock); + + pr("files\n"); + files = &au_sbi(sb)->si_files; + hlist_bl_lock(files); + hlist_bl_for_each_entry(finfo, pos, files, fi_hlist) { + umode_t mode; + + file = finfo->fi_file; + mode = file_inode(file)->i_mode; + if (!special_file(mode)) + au_dpri_file(file); + } + hlist_bl_unlock(files); + pr("done\n"); + +#undef pr + au_plevel = plevel; +} + +/* ---------------------------------------------------------------------- */ + +/* module parameter */ +static char *aufs_sysrq_key = "a"; +module_param_named(sysrq, aufs_sysrq_key, charp, 0444); +MODULE_PARM_DESC(sysrq, "MagicSysRq key for " AUFS_NAME); + +static void au_sysrq(int key __maybe_unused) +{ + struct au_sbinfo *sbinfo; + struct hlist_bl_node *pos; + + lockdep_off(); + au_sbilist_lock(); + hlist_bl_for_each_entry(sbinfo, pos, &au_sbilist, si_list) + sysrq_sb(sbinfo->si_sb); + au_sbilist_unlock(); + lockdep_on(); +} + +static struct sysrq_key_op au_sysrq_op = { + .handler = au_sysrq, + .help_msg = "Aufs", + .action_msg = "Aufs", + .enable_mask = SYSRQ_ENABLE_DUMP +}; + +/* ---------------------------------------------------------------------- */ + +int __init au_sysrq_init(void) +{ + int err; + char key; + + err = -1; + key = *aufs_sysrq_key; + if ('a' <= key && key <= 'z') + err = register_sysrq_key(key, &au_sysrq_op); + if (unlikely(err)) + pr_err("err %d, sysrq=%c\n", err, key); + return err; +} + +void au_sysrq_fin(void) +{ + int err; + + err = unregister_sysrq_key(*aufs_sysrq_key, &au_sysrq_op); + if (unlikely(err)) + pr_err("err %d (ignored)\n", err); +} diff --git a/fs/aufs/vdir.c b/fs/aufs/vdir.c new file mode 100644 index 0000000000000..a3f709ee7475f --- /dev/null +++ b/fs/aufs/vdir.c @@ -0,0 +1,896 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * virtual or vertical directory + */ + +#include +#include "aufs.h" + +static unsigned int calc_size(int nlen) +{ + return ALIGN(sizeof(struct au_vdir_de) + nlen, sizeof(ino_t)); +} + +static int set_deblk_end(union au_vdir_deblk_p *p, + union au_vdir_deblk_p *deblk_end) +{ + if (calc_size(0) <= deblk_end->deblk - p->deblk) { + p->de->de_str.len = 0; + /* smp_mb(); */ + return 0; + } + return -1; /* error */ +} + +/* returns true or false */ +static int is_deblk_end(union au_vdir_deblk_p *p, + union au_vdir_deblk_p *deblk_end) +{ + if (calc_size(0) <= deblk_end->deblk - p->deblk) + return !p->de->de_str.len; + return 1; +} + +static unsigned char *last_deblk(struct au_vdir *vdir) +{ + return vdir->vd_deblk[vdir->vd_nblk - 1]; +} + +/* ---------------------------------------------------------------------- */ + +/* estimate the appropriate size for name hash table */ +unsigned int au_rdhash_est(loff_t sz) +{ + unsigned int n; + + n = UINT_MAX; + sz >>= 10; + if (sz < n) + n = sz; + if (sz < AUFS_RDHASH_DEF) + n = AUFS_RDHASH_DEF; + /* pr_info("n %u\n", n); */ + return n; +} + +/* + * the allocated memory has to be freed by + * au_nhash_wh_free() or au_nhash_de_free(). + */ +int au_nhash_alloc(struct au_nhash *nhash, unsigned int num_hash, gfp_t gfp) +{ + struct hlist_head *head; + unsigned int u; + size_t sz; + + sz = sizeof(*nhash->nh_head) * num_hash; + head = kmalloc(sz, gfp); + if (head) { + nhash->nh_num = num_hash; + nhash->nh_head = head; + for (u = 0; u < num_hash; u++) + INIT_HLIST_HEAD(head++); + return 0; /* success */ + } + + return -ENOMEM; +} + +static void nhash_count(struct hlist_head *head) +{ +#if 0 /* debugging */ + unsigned long n; + struct hlist_node *pos; + + n = 0; + hlist_for_each(pos, head) + n++; + pr_info("%lu\n", n); +#endif +} + +static void au_nhash_wh_do_free(struct hlist_head *head) +{ + struct au_vdir_wh *pos; + struct hlist_node *node; + + hlist_for_each_entry_safe(pos, node, head, wh_hash) + au_kfree_rcu(pos); +} + +static void au_nhash_de_do_free(struct hlist_head *head) +{ + struct au_vdir_dehstr *pos; + struct hlist_node *node; + + hlist_for_each_entry_safe(pos, node, head, hash) + au_cache_free_vdir_dehstr(pos); +} + +static void au_nhash_do_free(struct au_nhash *nhash, + void (*free)(struct hlist_head *head)) +{ + unsigned int n; + struct hlist_head *head; + + n = nhash->nh_num; + if (!n) + return; + + head = nhash->nh_head; + while (n-- > 0) { + nhash_count(head); + free(head++); + } + au_kfree_try_rcu(nhash->nh_head); +} + +void au_nhash_wh_free(struct au_nhash *whlist) +{ + au_nhash_do_free(whlist, au_nhash_wh_do_free); +} + +static void au_nhash_de_free(struct au_nhash *delist) +{ + au_nhash_do_free(delist, au_nhash_de_do_free); +} + +/* ---------------------------------------------------------------------- */ + +int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt, + int limit) +{ + int num; + unsigned int u, n; + struct hlist_head *head; + struct au_vdir_wh *pos; + + num = 0; + n = whlist->nh_num; + head = whlist->nh_head; + for (u = 0; u < n; u++, head++) + hlist_for_each_entry(pos, head, wh_hash) + if (pos->wh_bindex == btgt && ++num > limit) + return 1; + return 0; +} + +static struct hlist_head *au_name_hash(struct au_nhash *nhash, + unsigned char *name, + unsigned int len) +{ + unsigned int v; + /* const unsigned int magic_bit = 12; */ + + AuDebugOn(!nhash->nh_num || !nhash->nh_head); + + v = 0; + if (len > 8) + len = 8; + while (len--) + v += *name++; + /* v = hash_long(v, magic_bit); */ + v %= nhash->nh_num; + return nhash->nh_head + v; +} + +static int au_nhash_test_name(struct au_vdir_destr *str, const char *name, + int nlen) +{ + return str->len == nlen && !memcmp(str->name, name, nlen); +} + +/* returns found or not */ +int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int nlen) +{ + struct hlist_head *head; + struct au_vdir_wh *pos; + struct au_vdir_destr *str; + + head = au_name_hash(whlist, name, nlen); + hlist_for_each_entry(pos, head, wh_hash) { + str = &pos->wh_str; + AuDbg("%.*s\n", str->len, str->name); + if (au_nhash_test_name(str, name, nlen)) + return 1; + } + return 0; +} + +/* returns found(true) or not */ +static int test_known(struct au_nhash *delist, char *name, int nlen) +{ + struct hlist_head *head; + struct au_vdir_dehstr *pos; + struct au_vdir_destr *str; + + head = au_name_hash(delist, name, nlen); + hlist_for_each_entry(pos, head, hash) { + str = pos->str; + AuDbg("%.*s\n", str->len, str->name); + if (au_nhash_test_name(str, name, nlen)) + return 1; + } + return 0; +} + +static void au_shwh_init_wh(struct au_vdir_wh *wh, ino_t ino, + unsigned char d_type) +{ +#ifdef CONFIG_AUFS_SHWH + wh->wh_ino = ino; + wh->wh_type = d_type; +#endif +} + +/* ---------------------------------------------------------------------- */ + +int au_nhash_append_wh(struct au_nhash *whlist, char *name, int nlen, ino_t ino, + unsigned int d_type, aufs_bindex_t bindex, + unsigned char shwh) +{ + int err; + struct au_vdir_destr *str; + struct au_vdir_wh *wh; + + AuDbg("%.*s\n", nlen, name); + AuDebugOn(!whlist->nh_num || !whlist->nh_head); + + err = -ENOMEM; + wh = kmalloc(sizeof(*wh) + nlen, GFP_NOFS); + if (unlikely(!wh)) + goto out; + + err = 0; + wh->wh_bindex = bindex; + if (shwh) + au_shwh_init_wh(wh, ino, d_type); + str = &wh->wh_str; + str->len = nlen; + memcpy(str->name, name, nlen); + hlist_add_head(&wh->wh_hash, au_name_hash(whlist, name, nlen)); + /* smp_mb(); */ + +out: + return err; +} + +static int append_deblk(struct au_vdir *vdir) +{ + int err; + unsigned long ul; + const unsigned int deblk_sz = vdir->vd_deblk_sz; + union au_vdir_deblk_p p, deblk_end; + unsigned char **o; + + err = -ENOMEM; + o = au_krealloc(vdir->vd_deblk, sizeof(*o) * (vdir->vd_nblk + 1), + GFP_NOFS, /*may_shrink*/0); + if (unlikely(!o)) + goto out; + + vdir->vd_deblk = o; + p.deblk = kmalloc(deblk_sz, GFP_NOFS); + if (p.deblk) { + ul = vdir->vd_nblk++; + vdir->vd_deblk[ul] = p.deblk; + vdir->vd_last.ul = ul; + vdir->vd_last.p.deblk = p.deblk; + deblk_end.deblk = p.deblk + deblk_sz; + err = set_deblk_end(&p, &deblk_end); + } + +out: + return err; +} + +static int append_de(struct au_vdir *vdir, char *name, int nlen, ino_t ino, + unsigned int d_type, struct au_nhash *delist) +{ + int err; + unsigned int sz; + const unsigned int deblk_sz = vdir->vd_deblk_sz; + union au_vdir_deblk_p p, *room, deblk_end; + struct au_vdir_dehstr *dehstr; + + p.deblk = last_deblk(vdir); + deblk_end.deblk = p.deblk + deblk_sz; + room = &vdir->vd_last.p; + AuDebugOn(room->deblk < p.deblk || deblk_end.deblk <= room->deblk + || !is_deblk_end(room, &deblk_end)); + + sz = calc_size(nlen); + if (unlikely(sz > deblk_end.deblk - room->deblk)) { + err = append_deblk(vdir); + if (unlikely(err)) + goto out; + + p.deblk = last_deblk(vdir); + deblk_end.deblk = p.deblk + deblk_sz; + /* smp_mb(); */ + AuDebugOn(room->deblk != p.deblk); + } + + err = -ENOMEM; + dehstr = au_cache_alloc_vdir_dehstr(); + if (unlikely(!dehstr)) + goto out; + + dehstr->str = &room->de->de_str; + hlist_add_head(&dehstr->hash, au_name_hash(delist, name, nlen)); + room->de->de_ino = ino; + room->de->de_type = d_type; + room->de->de_str.len = nlen; + memcpy(room->de->de_str.name, name, nlen); + + err = 0; + room->deblk += sz; + if (unlikely(set_deblk_end(room, &deblk_end))) + err = append_deblk(vdir); + /* smp_mb(); */ + +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +void au_vdir_free(struct au_vdir *vdir) +{ + unsigned char **deblk; + + deblk = vdir->vd_deblk; + while (vdir->vd_nblk--) + au_kfree_try_rcu(*deblk++); + au_kfree_try_rcu(vdir->vd_deblk); + au_cache_free_vdir(vdir); +} + +static struct au_vdir *alloc_vdir(struct file *file) +{ + struct au_vdir *vdir; + struct super_block *sb; + int err; + + sb = file->f_path.dentry->d_sb; + SiMustAnyLock(sb); + + err = -ENOMEM; + vdir = au_cache_alloc_vdir(); + if (unlikely(!vdir)) + goto out; + + vdir->vd_deblk = kzalloc(sizeof(*vdir->vd_deblk), GFP_NOFS); + if (unlikely(!vdir->vd_deblk)) + goto out_free; + + vdir->vd_deblk_sz = au_sbi(sb)->si_rdblk; + if (!vdir->vd_deblk_sz) { + /* estimate the appropriate size for deblk */ + vdir->vd_deblk_sz = au_dir_size(file, /*dentry*/NULL); + /* pr_info("vd_deblk_sz %u\n", vdir->vd_deblk_sz); */ + } + vdir->vd_nblk = 0; + vdir->vd_version = 0; + vdir->vd_jiffy = 0; + err = append_deblk(vdir); + if (!err) + return vdir; /* success */ + + au_kfree_try_rcu(vdir->vd_deblk); + +out_free: + au_cache_free_vdir(vdir); +out: + vdir = ERR_PTR(err); + return vdir; +} + +static int reinit_vdir(struct au_vdir *vdir) +{ + int err; + union au_vdir_deblk_p p, deblk_end; + + while (vdir->vd_nblk > 1) { + au_kfree_try_rcu(vdir->vd_deblk[vdir->vd_nblk - 1]); + /* vdir->vd_deblk[vdir->vd_nblk - 1] = NULL; */ + vdir->vd_nblk--; + } + p.deblk = vdir->vd_deblk[0]; + deblk_end.deblk = p.deblk + vdir->vd_deblk_sz; + err = set_deblk_end(&p, &deblk_end); + /* keep vd_dblk_sz */ + vdir->vd_last.ul = 0; + vdir->vd_last.p.deblk = vdir->vd_deblk[0]; + vdir->vd_version = 0; + vdir->vd_jiffy = 0; + /* smp_mb(); */ + return err; +} + +/* ---------------------------------------------------------------------- */ + +#define AuFillVdir_CALLED 1 +#define AuFillVdir_WHABLE (1 << 1) +#define AuFillVdir_SHWH (1 << 2) +#define au_ftest_fillvdir(flags, name) ((flags) & AuFillVdir_##name) +#define au_fset_fillvdir(flags, name) \ + do { (flags) |= AuFillVdir_##name; } while (0) +#define au_fclr_fillvdir(flags, name) \ + do { (flags) &= ~AuFillVdir_##name; } while (0) + +#ifndef CONFIG_AUFS_SHWH +#undef AuFillVdir_SHWH +#define AuFillVdir_SHWH 0 +#endif + +struct fillvdir_arg { + struct dir_context ctx; + struct file *file; + struct au_vdir *vdir; + struct au_nhash delist; + struct au_nhash whlist; + aufs_bindex_t bindex; + unsigned int flags; + int err; +}; + +static int fillvdir(struct dir_context *ctx, const char *__name, int nlen, + loff_t offset __maybe_unused, u64 h_ino, + unsigned int d_type) +{ + struct fillvdir_arg *arg = container_of(ctx, struct fillvdir_arg, ctx); + char *name = (void *)__name; + struct super_block *sb; + ino_t ino; + const unsigned char shwh = !!au_ftest_fillvdir(arg->flags, SHWH); + + arg->err = 0; + sb = arg->file->f_path.dentry->d_sb; + au_fset_fillvdir(arg->flags, CALLED); + /* smp_mb(); */ + if (nlen <= AUFS_WH_PFX_LEN + || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { + if (test_known(&arg->delist, name, nlen) + || au_nhash_test_known_wh(&arg->whlist, name, nlen)) + goto out; /* already exists or whiteouted */ + + arg->err = au_ino(sb, arg->bindex, h_ino, d_type, &ino); + if (!arg->err) { + if (unlikely(nlen > AUFS_MAX_NAMELEN)) + d_type = DT_UNKNOWN; + arg->err = append_de(arg->vdir, name, nlen, ino, + d_type, &arg->delist); + } + } else if (au_ftest_fillvdir(arg->flags, WHABLE)) { + name += AUFS_WH_PFX_LEN; + nlen -= AUFS_WH_PFX_LEN; + if (au_nhash_test_known_wh(&arg->whlist, name, nlen)) + goto out; /* already whiteouted */ + + ino = 0; /* just to suppress a warning */ + if (shwh) + arg->err = au_wh_ino(sb, arg->bindex, h_ino, d_type, + &ino); + if (!arg->err) { + if (nlen <= AUFS_MAX_NAMELEN + AUFS_WH_PFX_LEN) + d_type = DT_UNKNOWN; + arg->err = au_nhash_append_wh + (&arg->whlist, name, nlen, ino, d_type, + arg->bindex, shwh); + } + } + +out: + if (!arg->err) + arg->vdir->vd_jiffy = jiffies; + /* smp_mb(); */ + AuTraceErr(arg->err); + return arg->err; +} + +static int au_handle_shwh(struct super_block *sb, struct au_vdir *vdir, + struct au_nhash *whlist, struct au_nhash *delist) +{ +#ifdef CONFIG_AUFS_SHWH + int err; + unsigned int nh, u; + struct hlist_head *head; + struct au_vdir_wh *pos; + struct hlist_node *n; + char *p, *o; + struct au_vdir_destr *destr; + + AuDebugOn(!au_opt_test(au_mntflags(sb), SHWH)); + + err = -ENOMEM; + o = p = (void *)__get_free_page(GFP_NOFS); + if (unlikely(!p)) + goto out; + + err = 0; + nh = whlist->nh_num; + memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); + p += AUFS_WH_PFX_LEN; + for (u = 0; u < nh; u++) { + head = whlist->nh_head + u; + hlist_for_each_entry_safe(pos, n, head, wh_hash) { + destr = &pos->wh_str; + memcpy(p, destr->name, destr->len); + err = append_de(vdir, o, destr->len + AUFS_WH_PFX_LEN, + pos->wh_ino, pos->wh_type, delist); + if (unlikely(err)) + break; + } + } + + free_page((unsigned long)o); + +out: + AuTraceErr(err); + return err; +#else + return 0; +#endif +} + +static int au_do_read_vdir(struct fillvdir_arg *arg) +{ + int err; + unsigned int rdhash; + loff_t offset; + aufs_bindex_t bbot, bindex, btop; + unsigned char shwh; + struct file *hf, *file; + struct super_block *sb; + + file = arg->file; + sb = file->f_path.dentry->d_sb; + SiMustAnyLock(sb); + + rdhash = au_sbi(sb)->si_rdhash; + if (!rdhash) + rdhash = au_rdhash_est(au_dir_size(file, /*dentry*/NULL)); + err = au_nhash_alloc(&arg->delist, rdhash, GFP_NOFS); + if (unlikely(err)) + goto out; + err = au_nhash_alloc(&arg->whlist, rdhash, GFP_NOFS); + if (unlikely(err)) + goto out_delist; + + err = 0; + arg->flags = 0; + shwh = 0; + if (au_opt_test(au_mntflags(sb), SHWH)) { + shwh = 1; + au_fset_fillvdir(arg->flags, SHWH); + } + btop = au_fbtop(file); + bbot = au_fbbot_dir(file); + for (bindex = btop; !err && bindex <= bbot; bindex++) { + hf = au_hf_dir(file, bindex); + if (!hf) + continue; + + offset = vfsub_llseek(hf, 0, SEEK_SET); + err = offset; + if (unlikely(offset)) + break; + + arg->bindex = bindex; + au_fclr_fillvdir(arg->flags, WHABLE); + if (shwh + || (bindex != bbot + && au_br_whable(au_sbr_perm(sb, bindex)))) + au_fset_fillvdir(arg->flags, WHABLE); + do { + arg->err = 0; + au_fclr_fillvdir(arg->flags, CALLED); + /* smp_mb(); */ + err = vfsub_iterate_dir(hf, &arg->ctx); + if (err >= 0) + err = arg->err; + } while (!err && au_ftest_fillvdir(arg->flags, CALLED)); + + /* + * dir_relax() may be good for concurrency, but aufs should not + * use it since it will cause a lockdep problem. + */ + } + + if (!err && shwh) + err = au_handle_shwh(sb, arg->vdir, &arg->whlist, &arg->delist); + + au_nhash_wh_free(&arg->whlist); + +out_delist: + au_nhash_de_free(&arg->delist); +out: + return err; +} + +static int read_vdir(struct file *file, int may_read) +{ + int err; + unsigned long expire; + unsigned char do_read; + struct fillvdir_arg arg = { + .ctx = { + .actor = fillvdir + } + }; + struct inode *inode; + struct au_vdir *vdir, *allocated; + + err = 0; + inode = file_inode(file); + IMustLock(inode); + IiMustWriteLock(inode); + SiMustAnyLock(inode->i_sb); + + allocated = NULL; + do_read = 0; + expire = au_sbi(inode->i_sb)->si_rdcache; + vdir = au_ivdir(inode); + if (!vdir) { + do_read = 1; + vdir = alloc_vdir(file); + err = PTR_ERR(vdir); + if (IS_ERR(vdir)) + goto out; + err = 0; + allocated = vdir; + } else if (may_read + && (!inode_eq_iversion(inode, vdir->vd_version) + || time_after(jiffies, vdir->vd_jiffy + expire))) { + do_read = 1; + err = reinit_vdir(vdir); + if (unlikely(err)) + goto out; + } + + if (!do_read) + return 0; /* success */ + + arg.file = file; + arg.vdir = vdir; + err = au_do_read_vdir(&arg); + if (!err) { + /* file->f_pos = 0; */ /* todo: ctx->pos? */ + vdir->vd_version = inode_query_iversion(inode); + vdir->vd_last.ul = 0; + vdir->vd_last.p.deblk = vdir->vd_deblk[0]; + if (allocated) + au_set_ivdir(inode, allocated); + } else if (allocated) + au_vdir_free(allocated); + +out: + return err; +} + +static int copy_vdir(struct au_vdir *tgt, struct au_vdir *src) +{ + int err, rerr; + unsigned long ul, n; + const unsigned int deblk_sz = src->vd_deblk_sz; + + AuDebugOn(tgt->vd_nblk != 1); + + err = -ENOMEM; + if (tgt->vd_nblk < src->vd_nblk) { + unsigned char **p; + + p = au_krealloc(tgt->vd_deblk, sizeof(*p) * src->vd_nblk, + GFP_NOFS, /*may_shrink*/0); + if (unlikely(!p)) + goto out; + tgt->vd_deblk = p; + } + + if (tgt->vd_deblk_sz != deblk_sz) { + unsigned char *p; + + tgt->vd_deblk_sz = deblk_sz; + p = au_krealloc(tgt->vd_deblk[0], deblk_sz, GFP_NOFS, + /*may_shrink*/1); + if (unlikely(!p)) + goto out; + tgt->vd_deblk[0] = p; + } + memcpy(tgt->vd_deblk[0], src->vd_deblk[0], deblk_sz); + tgt->vd_version = src->vd_version; + tgt->vd_jiffy = src->vd_jiffy; + + n = src->vd_nblk; + for (ul = 1; ul < n; ul++) { + tgt->vd_deblk[ul] = kmemdup(src->vd_deblk[ul], deblk_sz, + GFP_NOFS); + if (unlikely(!tgt->vd_deblk[ul])) + goto out; + tgt->vd_nblk++; + } + tgt->vd_nblk = n; + tgt->vd_last.ul = tgt->vd_last.ul; + tgt->vd_last.p.deblk = tgt->vd_deblk[tgt->vd_last.ul]; + tgt->vd_last.p.deblk += src->vd_last.p.deblk + - src->vd_deblk[src->vd_last.ul]; + /* smp_mb(); */ + return 0; /* success */ + +out: + rerr = reinit_vdir(tgt); + BUG_ON(rerr); + return err; +} + +int au_vdir_init(struct file *file) +{ + int err; + struct inode *inode; + struct au_vdir *vdir_cache, *allocated; + + /* test file->f_pos here instead of ctx->pos */ + err = read_vdir(file, !file->f_pos); + if (unlikely(err)) + goto out; + + allocated = NULL; + vdir_cache = au_fvdir_cache(file); + if (!vdir_cache) { + vdir_cache = alloc_vdir(file); + err = PTR_ERR(vdir_cache); + if (IS_ERR(vdir_cache)) + goto out; + allocated = vdir_cache; + } else if (!file->f_pos && vdir_cache->vd_version != file->f_version) { + /* test file->f_pos here instead of ctx->pos */ + err = reinit_vdir(vdir_cache); + if (unlikely(err)) + goto out; + } else + return 0; /* success */ + + inode = file_inode(file); + err = copy_vdir(vdir_cache, au_ivdir(inode)); + if (!err) { + file->f_version = inode_query_iversion(inode); + if (allocated) + au_set_fvdir_cache(file, allocated); + } else if (allocated) + au_vdir_free(allocated); + +out: + return err; +} + +static loff_t calc_offset(struct au_vdir *vdir) +{ + loff_t offset; + union au_vdir_deblk_p p; + + p.deblk = vdir->vd_deblk[vdir->vd_last.ul]; + offset = vdir->vd_last.p.deblk - p.deblk; + offset += vdir->vd_deblk_sz * vdir->vd_last.ul; + return offset; +} + +/* returns true or false */ +static int seek_vdir(struct file *file, struct dir_context *ctx) +{ + int valid; + unsigned int deblk_sz; + unsigned long ul, n; + loff_t offset; + union au_vdir_deblk_p p, deblk_end; + struct au_vdir *vdir_cache; + + valid = 1; + vdir_cache = au_fvdir_cache(file); + offset = calc_offset(vdir_cache); + AuDbg("offset %lld\n", offset); + if (ctx->pos == offset) + goto out; + + vdir_cache->vd_last.ul = 0; + vdir_cache->vd_last.p.deblk = vdir_cache->vd_deblk[0]; + if (!ctx->pos) + goto out; + + valid = 0; + deblk_sz = vdir_cache->vd_deblk_sz; + ul = div64_u64(ctx->pos, deblk_sz); + AuDbg("ul %lu\n", ul); + if (ul >= vdir_cache->vd_nblk) + goto out; + + n = vdir_cache->vd_nblk; + for (; ul < n; ul++) { + p.deblk = vdir_cache->vd_deblk[ul]; + deblk_end.deblk = p.deblk + deblk_sz; + offset = ul; + offset *= deblk_sz; + while (!is_deblk_end(&p, &deblk_end) && offset < ctx->pos) { + unsigned int l; + + l = calc_size(p.de->de_str.len); + offset += l; + p.deblk += l; + } + if (!is_deblk_end(&p, &deblk_end)) { + valid = 1; + vdir_cache->vd_last.ul = ul; + vdir_cache->vd_last.p = p; + break; + } + } + +out: + /* smp_mb(); */ + if (!valid) + AuDbg("valid %d\n", !valid); + return valid; +} + +int au_vdir_fill_de(struct file *file, struct dir_context *ctx) +{ + unsigned int l, deblk_sz; + union au_vdir_deblk_p deblk_end; + struct au_vdir *vdir_cache; + struct au_vdir_de *de; + + if (!seek_vdir(file, ctx)) + return 0; + + vdir_cache = au_fvdir_cache(file); + deblk_sz = vdir_cache->vd_deblk_sz; + while (1) { + deblk_end.deblk = vdir_cache->vd_deblk[vdir_cache->vd_last.ul]; + deblk_end.deblk += deblk_sz; + while (!is_deblk_end(&vdir_cache->vd_last.p, &deblk_end)) { + de = vdir_cache->vd_last.p.de; + AuDbg("%.*s, off%lld, i%lu, dt%d\n", + de->de_str.len, de->de_str.name, ctx->pos, + (unsigned long)de->de_ino, de->de_type); + if (unlikely(!dir_emit(ctx, de->de_str.name, + de->de_str.len, de->de_ino, + de->de_type))) { + /* todo: ignore the error caused by udba? */ + /* return err; */ + return 0; + } + + l = calc_size(de->de_str.len); + vdir_cache->vd_last.p.deblk += l; + ctx->pos += l; + } + if (vdir_cache->vd_last.ul < vdir_cache->vd_nblk - 1) { + vdir_cache->vd_last.ul++; + vdir_cache->vd_last.p.deblk + = vdir_cache->vd_deblk[vdir_cache->vd_last.ul]; + ctx->pos = deblk_sz * vdir_cache->vd_last.ul; + continue; + } + break; + } + + /* smp_mb(); */ + return 0; +} diff --git a/fs/aufs/vfsub.c b/fs/aufs/vfsub.c new file mode 100644 index 0000000000000..a85774c99e277 --- /dev/null +++ b/fs/aufs/vfsub.c @@ -0,0 +1,889 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * sub-routines for VFS + */ + +#include +#include +#include +#include +#include "aufs.h" + +#ifdef CONFIG_AUFS_BR_FUSE +int vfsub_test_mntns(struct vfsmount *mnt, struct super_block *h_sb) +{ + if (!au_test_fuse(h_sb) || !au_userns) + return 0; + + return is_current_mnt_ns(mnt) ? 0 : -EACCES; +} +#endif + +int vfsub_sync_filesystem(struct super_block *h_sb) +{ + int err; + + lockdep_off(); + down_read(&h_sb->s_umount); + err = sync_filesystem(h_sb); + up_read(&h_sb->s_umount); + lockdep_on(); + + return err; +} + +/* ---------------------------------------------------------------------- */ + +int vfsub_update_h_iattr(struct path *h_path, int *did) +{ + int err; + struct kstat st; + struct super_block *h_sb; + + /* + * Always needs h_path->mnt for LSM or FUSE branch. + */ + AuDebugOn(!h_path->mnt); + + /* for remote fs, leave work for its getattr or d_revalidate */ + /* for bad i_attr fs, handle them in aufs_getattr() */ + /* still some fs may acquire i_mutex. we need to skip them */ + err = 0; + if (!did) + did = &err; + h_sb = h_path->dentry->d_sb; + *did = (!au_test_fs_remote(h_sb) && au_test_fs_refresh_iattr(h_sb)); + if (*did) + err = vfsub_getattr(h_path, &st); + + return err; +} + +/* ---------------------------------------------------------------------- */ + +struct file *vfsub_dentry_open(struct path *path, int flags) +{ + return dentry_open(path, flags /* | __FMODE_NONOTIFY */, + current_cred()); +} + +struct file *vfsub_filp_open(const char *path, int oflags, int mode) +{ + struct file *file; + + lockdep_off(); + file = filp_open(path, + oflags /* | __FMODE_NONOTIFY */, + mode); + lockdep_on(); + if (IS_ERR(file)) + goto out; + vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ + +out: + return file; +} + +/* + * Ideally this function should call VFS:do_last() in order to keep all its + * checkings. But it is very hard for aufs to regenerate several VFS internal + * structure such as nameidata. This is a second (or third) best approach. + * cf. linux/fs/namei.c:do_last(), lookup_open() and atomic_open(). + */ +int vfsub_atomic_open(struct inode *dir, struct dentry *dentry, + struct vfsub_aopen_args *args) +{ + int err; + struct au_branch *br = args->br; + struct file *file = args->file; + /* copied from linux/fs/namei.c:atomic_open() */ + struct dentry *const DENTRY_NOT_SET = (void *)-1UL; + + IMustLock(dir); + AuDebugOn(!dir->i_op->atomic_open); + + err = au_br_test_oflag(args->open_flag, br); + if (unlikely(err)) + goto out; + + au_lcnt_inc(&br->br_nfiles); + file->f_path.dentry = DENTRY_NOT_SET; + file->f_path.mnt = au_br_mnt(br); + AuDbg("%ps\n", dir->i_op->atomic_open); + err = dir->i_op->atomic_open(dir, dentry, file, args->open_flag, + args->create_mode); + if (unlikely(err < 0)) { + au_lcnt_dec(&br->br_nfiles); + goto out; + } + + /* temporary workaround for nfsv4 branch */ + if (au_test_nfs(dir->i_sb)) + nfs_mark_for_revalidate(dir); + + if (file->f_mode & FMODE_CREATED) + fsnotify_create(dir, dentry); + if (!(file->f_mode & FMODE_OPENED)) { + au_lcnt_dec(&br->br_nfiles); + goto out; + } + + /* todo: call VFS:may_open() here */ + /* todo: ima_file_check() too? */ + if (!err && (args->open_flag & __FMODE_EXEC)) + err = deny_write_access(file); + if (!err) + fsnotify_open(file); + else + au_lcnt_dec(&br->br_nfiles); + /* note that the file is created and still opened */ + +out: + return err; +} + +int vfsub_kern_path(const char *name, unsigned int flags, struct path *path) +{ + int err; + + err = kern_path(name, flags, path); + if (!err && d_is_positive(path->dentry)) + vfsub_update_h_iattr(path, /*did*/NULL); /*ignore*/ + return err; +} + +struct dentry *vfsub_lookup_one_len_unlocked(const char *name, + struct path *ppath, int len) +{ + struct path path; + + path.dentry = lookup_one_len_unlocked(name, ppath->dentry, len); + if (IS_ERR(path.dentry)) + goto out; + if (d_is_positive(path.dentry)) { + path.mnt = ppath->mnt; + vfsub_update_h_iattr(&path, /*did*/NULL); /*ignore*/ + } + +out: + AuTraceErrPtr(path.dentry); + return path.dentry; +} + +struct dentry *vfsub_lookup_one_len(const char *name, struct path *ppath, + int len) +{ + struct path path; + + /* VFS checks it too, but by WARN_ON_ONCE() */ + IMustLock(d_inode(ppath->dentry)); + + path.dentry = lookup_one_len(name, ppath->dentry, len); + if (IS_ERR(path.dentry)) + goto out; + if (d_is_positive(path.dentry)) { + path.mnt = ppath->mnt; + vfsub_update_h_iattr(&path, /*did*/NULL); /*ignore*/ + } + +out: + AuTraceErrPtr(path.dentry); + return path.dentry; +} + +void vfsub_call_lkup_one(void *args) +{ + struct vfsub_lkup_one_args *a = args; + *a->errp = vfsub_lkup_one(a->name, a->ppath); +} + +/* ---------------------------------------------------------------------- */ + +struct dentry *vfsub_lock_rename(struct dentry *d1, struct au_hinode *hdir1, + struct dentry *d2, struct au_hinode *hdir2) +{ + struct dentry *d; + + lockdep_off(); + d = lock_rename(d1, d2); + lockdep_on(); + au_hn_suspend(hdir1); + if (hdir1 != hdir2) + au_hn_suspend(hdir2); + + return d; +} + +void vfsub_unlock_rename(struct dentry *d1, struct au_hinode *hdir1, + struct dentry *d2, struct au_hinode *hdir2) +{ + au_hn_resume(hdir1); + if (hdir1 != hdir2) + au_hn_resume(hdir2); + lockdep_off(); + unlock_rename(d1, d2); + lockdep_on(); +} + +/* ---------------------------------------------------------------------- */ + +int vfsub_create(struct inode *dir, struct path *path, int mode, bool want_excl) +{ + int err; + struct dentry *d; + + IMustLock(dir); + + d = path->dentry; + path->dentry = d->d_parent; + err = security_path_mknod(path, d, mode, 0); + path->dentry = d; + if (unlikely(err)) + goto out; + + lockdep_off(); + err = vfs_create(dir, path->dentry, mode, want_excl); + lockdep_on(); + if (!err) { + struct path tmp = *path; + int did; + + vfsub_update_h_iattr(&tmp, &did); + if (did) { + tmp.dentry = path->dentry->d_parent; + vfsub_update_h_iattr(&tmp, /*did*/NULL); + } + /*ignore*/ + } + +out: + return err; +} + +int vfsub_symlink(struct inode *dir, struct path *path, const char *symname) +{ + int err; + struct dentry *d; + + IMustLock(dir); + + d = path->dentry; + path->dentry = d->d_parent; + err = security_path_symlink(path, d, symname); + path->dentry = d; + if (unlikely(err)) + goto out; + + lockdep_off(); + err = vfs_symlink(dir, path->dentry, symname); + lockdep_on(); + if (!err) { + struct path tmp = *path; + int did; + + vfsub_update_h_iattr(&tmp, &did); + if (did) { + tmp.dentry = path->dentry->d_parent; + vfsub_update_h_iattr(&tmp, /*did*/NULL); + } + /*ignore*/ + } + +out: + return err; +} + +int vfsub_mknod(struct inode *dir, struct path *path, int mode, dev_t dev) +{ + int err; + struct dentry *d; + + IMustLock(dir); + + d = path->dentry; + path->dentry = d->d_parent; + err = security_path_mknod(path, d, mode, new_encode_dev(dev)); + path->dentry = d; + if (unlikely(err)) + goto out; + + lockdep_off(); + err = vfs_mknod(dir, path->dentry, mode, dev); + lockdep_on(); + if (!err) { + struct path tmp = *path; + int did; + + vfsub_update_h_iattr(&tmp, &did); + if (did) { + tmp.dentry = path->dentry->d_parent; + vfsub_update_h_iattr(&tmp, /*did*/NULL); + } + /*ignore*/ + } + +out: + return err; +} + +static int au_test_nlink(struct inode *inode) +{ + const unsigned int link_max = UINT_MAX >> 1; /* rough margin */ + + if (!au_test_fs_no_limit_nlink(inode->i_sb) + || inode->i_nlink < link_max) + return 0; + return -EMLINK; +} + +int vfsub_link(struct dentry *src_dentry, struct inode *dir, struct path *path, + struct inode **delegated_inode) +{ + int err; + struct dentry *d; + + IMustLock(dir); + + err = au_test_nlink(d_inode(src_dentry)); + if (unlikely(err)) + return err; + + /* we don't call may_linkat() */ + d = path->dentry; + path->dentry = d->d_parent; + err = security_path_link(src_dentry, path, d); + path->dentry = d; + if (unlikely(err)) + goto out; + + lockdep_off(); + err = vfs_link(src_dentry, dir, path->dentry, delegated_inode); + lockdep_on(); + if (!err) { + struct path tmp = *path; + int did; + + /* fuse has different memory inode for the same inumber */ + vfsub_update_h_iattr(&tmp, &did); + if (did) { + tmp.dentry = path->dentry->d_parent; + vfsub_update_h_iattr(&tmp, /*did*/NULL); + tmp.dentry = src_dentry; + vfsub_update_h_iattr(&tmp, /*did*/NULL); + } + /*ignore*/ + } + +out: + return err; +} + +int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry, + struct inode *dir, struct path *path, + struct inode **delegated_inode, unsigned int flags) +{ + int err; + struct path tmp = { + .mnt = path->mnt + }; + struct dentry *d; + + IMustLock(dir); + IMustLock(src_dir); + + d = path->dentry; + path->dentry = d->d_parent; + tmp.dentry = src_dentry->d_parent; + err = security_path_rename(&tmp, src_dentry, path, d, /*flags*/0); + path->dentry = d; + if (unlikely(err)) + goto out; + + lockdep_off(); + err = vfs_rename(src_dir, src_dentry, dir, path->dentry, + delegated_inode, flags); + lockdep_on(); + if (!err) { + int did; + + tmp.dentry = d->d_parent; + vfsub_update_h_iattr(&tmp, &did); + if (did) { + tmp.dentry = src_dentry; + vfsub_update_h_iattr(&tmp, /*did*/NULL); + tmp.dentry = src_dentry->d_parent; + vfsub_update_h_iattr(&tmp, /*did*/NULL); + } + /*ignore*/ + } + +out: + return err; +} + +int vfsub_mkdir(struct inode *dir, struct path *path, int mode) +{ + int err; + struct dentry *d; + + IMustLock(dir); + + d = path->dentry; + path->dentry = d->d_parent; + err = security_path_mkdir(path, d, mode); + path->dentry = d; + if (unlikely(err)) + goto out; + + lockdep_off(); + err = vfs_mkdir(dir, path->dentry, mode); + lockdep_on(); + if (!err) { + struct path tmp = *path; + int did; + + vfsub_update_h_iattr(&tmp, &did); + if (did) { + tmp.dentry = path->dentry->d_parent; + vfsub_update_h_iattr(&tmp, /*did*/NULL); + } + /*ignore*/ + } + +out: + return err; +} + +int vfsub_rmdir(struct inode *dir, struct path *path) +{ + int err; + struct dentry *d; + + IMustLock(dir); + + d = path->dentry; + path->dentry = d->d_parent; + err = security_path_rmdir(path, d); + path->dentry = d; + if (unlikely(err)) + goto out; + + lockdep_off(); + err = vfs_rmdir(dir, path->dentry); + lockdep_on(); + if (!err) { + struct path tmp = { + .dentry = path->dentry->d_parent, + .mnt = path->mnt + }; + + vfsub_update_h_iattr(&tmp, /*did*/NULL); /*ignore*/ + } + +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* todo: support mmap_sem? */ +ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count, + loff_t *ppos) +{ + ssize_t err; + + lockdep_off(); + err = vfs_read(file, ubuf, count, ppos); + lockdep_on(); + if (err >= 0) + vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ + return err; +} + +ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, + loff_t *ppos) +{ + ssize_t err; + + lockdep_off(); + err = kernel_read(file, kbuf, count, ppos); + lockdep_on(); + AuTraceErr(err); + if (err >= 0) + vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ + return err; +} + +ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count, + loff_t *ppos) +{ + ssize_t err; + + lockdep_off(); + err = vfs_write(file, ubuf, count, ppos); + lockdep_on(); + if (err >= 0) + vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ + return err; +} + +ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos) +{ + ssize_t err; + + lockdep_off(); + err = kernel_write(file, kbuf, count, ppos); + lockdep_on(); + if (err >= 0) + vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ + return err; +} + +int vfsub_flush(struct file *file, fl_owner_t id) +{ + int err; + + err = 0; + if (file->f_op->flush) { + if (!au_test_nfs(file->f_path.dentry->d_sb)) + err = file->f_op->flush(file, id); + else { + lockdep_off(); + err = file->f_op->flush(file, id); + lockdep_on(); + } + if (!err) + vfsub_update_h_iattr(&file->f_path, /*did*/NULL); + /*ignore*/ + } + return err; +} + +int vfsub_iterate_dir(struct file *file, struct dir_context *ctx) +{ + int err; + + AuDbg("%pD, ctx{%ps, %llu}\n", file, ctx->actor, ctx->pos); + + lockdep_off(); + err = iterate_dir(file, ctx); + lockdep_on(); + if (err >= 0) + vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ + + return err; +} + +long vfsub_splice_to(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags) +{ + long err; + + lockdep_off(); + err = do_splice_to(in, ppos, pipe, len, flags); + lockdep_on(); + file_accessed(in); + if (err >= 0) + vfsub_update_h_iattr(&in->f_path, /*did*/NULL); /*ignore*/ + return err; +} + +long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, + loff_t *ppos, size_t len, unsigned int flags) +{ + long err; + + lockdep_off(); + err = do_splice_from(pipe, out, ppos, len, flags); + lockdep_on(); + if (err >= 0) + vfsub_update_h_iattr(&out->f_path, /*did*/NULL); /*ignore*/ + return err; +} + +int vfsub_fsync(struct file *file, struct path *path, int datasync) +{ + int err; + + /* file can be NULL */ + lockdep_off(); + err = vfs_fsync(file, datasync); + lockdep_on(); + if (!err) { + if (!path) { + AuDebugOn(!file); + path = &file->f_path; + } + vfsub_update_h_iattr(path, /*did*/NULL); /*ignore*/ + } + return err; +} + +/* cf. open.c:do_sys_truncate() and do_sys_ftruncate() */ +int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr, + struct file *h_file) +{ + int err; + struct inode *h_inode; + struct super_block *h_sb; + + if (!h_file) { + err = vfsub_truncate(h_path, length); + goto out; + } + + h_inode = d_inode(h_path->dentry); + h_sb = h_inode->i_sb; + lockdep_off(); + sb_start_write(h_sb); + lockdep_on(); + err = locks_verify_truncate(h_inode, h_file, length); + if (!err) + err = security_path_truncate(h_path); + if (!err) { + lockdep_off(); + err = do_truncate(h_path->dentry, length, attr, h_file); + lockdep_on(); + } + lockdep_off(); + sb_end_write(h_sb); + lockdep_on(); + +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +struct au_vfsub_mkdir_args { + int *errp; + struct inode *dir; + struct path *path; + int mode; +}; + +static void au_call_vfsub_mkdir(void *args) +{ + struct au_vfsub_mkdir_args *a = args; + *a->errp = vfsub_mkdir(a->dir, a->path, a->mode); +} + +int vfsub_sio_mkdir(struct inode *dir, struct path *path, int mode) +{ + int err, do_sio, wkq_err; + + do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE); + if (!do_sio) { + lockdep_off(); + err = vfsub_mkdir(dir, path, mode); + lockdep_on(); + } else { + struct au_vfsub_mkdir_args args = { + .errp = &err, + .dir = dir, + .path = path, + .mode = mode + }; + wkq_err = au_wkq_wait(au_call_vfsub_mkdir, &args); + if (unlikely(wkq_err)) + err = wkq_err; + } + + return err; +} + +struct au_vfsub_rmdir_args { + int *errp; + struct inode *dir; + struct path *path; +}; + +static void au_call_vfsub_rmdir(void *args) +{ + struct au_vfsub_rmdir_args *a = args; + *a->errp = vfsub_rmdir(a->dir, a->path); +} + +int vfsub_sio_rmdir(struct inode *dir, struct path *path) +{ + int err, do_sio, wkq_err; + + do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE); + if (!do_sio) { + lockdep_off(); + err = vfsub_rmdir(dir, path); + lockdep_on(); + } else { + struct au_vfsub_rmdir_args args = { + .errp = &err, + .dir = dir, + .path = path + }; + wkq_err = au_wkq_wait(au_call_vfsub_rmdir, &args); + if (unlikely(wkq_err)) + err = wkq_err; + } + + return err; +} + +/* ---------------------------------------------------------------------- */ + +struct notify_change_args { + int *errp; + struct path *path; + struct iattr *ia; + struct inode **delegated_inode; +}; + +static void call_notify_change(void *args) +{ + struct notify_change_args *a = args; + struct inode *h_inode; + + h_inode = d_inode(a->path->dentry); + IMustLock(h_inode); + + *a->errp = -EPERM; + if (!IS_IMMUTABLE(h_inode) && !IS_APPEND(h_inode)) { + lockdep_off(); + *a->errp = notify_change(a->path->dentry, a->ia, + a->delegated_inode); + lockdep_on(); + if (!*a->errp) + vfsub_update_h_iattr(a->path, /*did*/NULL); /*ignore*/ + } + AuTraceErr(*a->errp); +} + +int vfsub_notify_change(struct path *path, struct iattr *ia, + struct inode **delegated_inode) +{ + int err; + struct notify_change_args args = { + .errp = &err, + .path = path, + .ia = ia, + .delegated_inode = delegated_inode + }; + + call_notify_change(&args); + + return err; +} + +int vfsub_sio_notify_change(struct path *path, struct iattr *ia, + struct inode **delegated_inode) +{ + int err, wkq_err; + struct notify_change_args args = { + .errp = &err, + .path = path, + .ia = ia, + .delegated_inode = delegated_inode + }; + + wkq_err = au_wkq_wait(call_notify_change, &args); + if (unlikely(wkq_err)) + err = wkq_err; + + return err; +} + +/* ---------------------------------------------------------------------- */ + +struct unlink_args { + int *errp; + struct inode *dir; + struct path *path; + struct inode **delegated_inode; +}; + +static void call_unlink(void *args) +{ + struct unlink_args *a = args; + struct dentry *d = a->path->dentry; + struct inode *h_inode; + const int stop_sillyrename = (au_test_nfs(d->d_sb) + && au_dcount(d) == 1); + + IMustLock(a->dir); + + a->path->dentry = d->d_parent; + *a->errp = security_path_unlink(a->path, d); + a->path->dentry = d; + if (unlikely(*a->errp)) + return; + + if (!stop_sillyrename) + dget(d); + h_inode = NULL; + if (d_is_positive(d)) { + h_inode = d_inode(d); + ihold(h_inode); + } + + lockdep_off(); + *a->errp = vfs_unlink(a->dir, d, a->delegated_inode); + lockdep_on(); + if (!*a->errp) { + struct path tmp = { + .dentry = d->d_parent, + .mnt = a->path->mnt + }; + vfsub_update_h_iattr(&tmp, /*did*/NULL); /*ignore*/ + } + + if (!stop_sillyrename) + dput(d); + if (h_inode) + iput(h_inode); + + AuTraceErr(*a->errp); +} + +/* + * @dir: must be locked. + * @dentry: target dentry. + */ +int vfsub_unlink(struct inode *dir, struct path *path, + struct inode **delegated_inode, int force) +{ + int err; + struct unlink_args args = { + .errp = &err, + .dir = dir, + .path = path, + .delegated_inode = delegated_inode + }; + + if (!force) + call_unlink(&args); + else { + int wkq_err; + + wkq_err = au_wkq_wait(call_unlink, &args); + if (unlikely(wkq_err)) + err = wkq_err; + } + + return err; +} diff --git a/fs/aufs/vfsub.h b/fs/aufs/vfsub.h new file mode 100644 index 0000000000000..54a0f98b60d68 --- /dev/null +++ b/fs/aufs/vfsub.h @@ -0,0 +1,354 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * sub-routines for VFS + */ + +#ifndef __AUFS_VFSUB_H__ +#define __AUFS_VFSUB_H__ + +#ifdef __KERNEL__ + +#include +#include +#include +#include +#include "debug.h" + +/* copied from linux/fs/internal.h */ +/* todo: BAD approach!! */ +extern void __mnt_drop_write(struct vfsmount *); +extern struct file *alloc_empty_file(int, const struct cred *); + +/* ---------------------------------------------------------------------- */ + +/* lock subclass for lower inode */ +/* default MAX_LOCKDEP_SUBCLASSES(8) is not enough */ +/* reduce? gave up. */ +enum { + AuLsc_I_Begin = I_MUTEX_PARENT2, /* 5 */ + AuLsc_I_PARENT, /* lower inode, parent first */ + AuLsc_I_PARENT2, /* copyup dirs */ + AuLsc_I_PARENT3, /* copyup wh */ + AuLsc_I_CHILD, + AuLsc_I_CHILD2, + AuLsc_I_End +}; + +/* to debug easier, do not make them inlined functions */ +#define MtxMustLock(mtx) AuDebugOn(!mutex_is_locked(mtx)) +#define IMustLock(i) AuDebugOn(!inode_is_locked(i)) + +/* ---------------------------------------------------------------------- */ + +static inline void vfsub_drop_nlink(struct inode *inode) +{ + AuDebugOn(!inode->i_nlink); + drop_nlink(inode); +} + +static inline void vfsub_dead_dir(struct inode *inode) +{ + AuDebugOn(!S_ISDIR(inode->i_mode)); + inode->i_flags |= S_DEAD; + clear_nlink(inode); +} + +static inline int vfsub_native_ro(struct inode *inode) +{ + return sb_rdonly(inode->i_sb) + || IS_RDONLY(inode) + /* || IS_APPEND(inode) */ + || IS_IMMUTABLE(inode); +} + +#ifdef CONFIG_AUFS_BR_FUSE +int vfsub_test_mntns(struct vfsmount *mnt, struct super_block *h_sb); +#else +AuStubInt0(vfsub_test_mntns, struct vfsmount *mnt, struct super_block *h_sb); +#endif + +int vfsub_sync_filesystem(struct super_block *h_sb); + +/* ---------------------------------------------------------------------- */ + +int vfsub_update_h_iattr(struct path *h_path, int *did); +struct file *vfsub_dentry_open(struct path *path, int flags); +struct file *vfsub_filp_open(const char *path, int oflags, int mode); +struct au_branch; +struct vfsub_aopen_args { + struct file *file; + unsigned int open_flag; + umode_t create_mode; + struct au_branch *br; +}; +int vfsub_atomic_open(struct inode *dir, struct dentry *dentry, + struct vfsub_aopen_args *args); +int vfsub_kern_path(const char *name, unsigned int flags, struct path *path); + +struct dentry *vfsub_lookup_one_len_unlocked(const char *name, + struct path *ppath, int len); +struct dentry *vfsub_lookup_one_len(const char *name, struct path *ppath, + int len); + +struct vfsub_lkup_one_args { + struct dentry **errp; + struct qstr *name; + struct path *ppath; +}; + +static inline struct dentry *vfsub_lkup_one(struct qstr *name, + struct path *ppath) +{ + return vfsub_lookup_one_len(name->name, ppath, name->len); +} + +void vfsub_call_lkup_one(void *args); + +/* ---------------------------------------------------------------------- */ + +static inline int vfsub_mnt_want_write(struct vfsmount *mnt) +{ + int err; + + lockdep_off(); + err = mnt_want_write(mnt); + lockdep_on(); + return err; +} + +static inline void vfsub_mnt_drop_write(struct vfsmount *mnt) +{ + lockdep_off(); + mnt_drop_write(mnt); + lockdep_on(); +} + +#if 0 /* reserved */ +static inline void vfsub_mnt_drop_write_file(struct file *file) +{ + lockdep_off(); + mnt_drop_write_file(file); + lockdep_on(); +} +#endif + +/* ---------------------------------------------------------------------- */ + +struct au_hinode; +struct dentry *vfsub_lock_rename(struct dentry *d1, struct au_hinode *hdir1, + struct dentry *d2, struct au_hinode *hdir2); +void vfsub_unlock_rename(struct dentry *d1, struct au_hinode *hdir1, + struct dentry *d2, struct au_hinode *hdir2); + +int vfsub_create(struct inode *dir, struct path *path, int mode, + bool want_excl); +int vfsub_symlink(struct inode *dir, struct path *path, + const char *symname); +int vfsub_mknod(struct inode *dir, struct path *path, int mode, dev_t dev); +int vfsub_link(struct dentry *src_dentry, struct inode *dir, + struct path *path, struct inode **delegated_inode); +int vfsub_rename(struct inode *src_hdir, struct dentry *src_dentry, + struct inode *hdir, struct path *path, + struct inode **delegated_inode, unsigned int flags); +int vfsub_mkdir(struct inode *dir, struct path *path, int mode); +int vfsub_rmdir(struct inode *dir, struct path *path); + +/* ---------------------------------------------------------------------- */ + +ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count, + loff_t *ppos); +ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, + loff_t *ppos); +ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count, + loff_t *ppos); +ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, + loff_t *ppos); +int vfsub_flush(struct file *file, fl_owner_t id); +int vfsub_iterate_dir(struct file *file, struct dir_context *ctx); + +static inline loff_t vfsub_f_size_read(struct file *file) +{ + return i_size_read(file_inode(file)); +} + +static inline unsigned int vfsub_file_flags(struct file *file) +{ + unsigned int flags; + + spin_lock(&file->f_lock); + flags = file->f_flags; + spin_unlock(&file->f_lock); + + return flags; +} + +static inline int vfsub_file_execed(struct file *file) +{ + /* todo: direct access f_flags */ + return !!(vfsub_file_flags(file) & __FMODE_EXEC); +} + +#if 0 /* reserved */ +static inline void vfsub_file_accessed(struct file *h_file) +{ + file_accessed(h_file); + vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL); /*ignore*/ +} +#endif + +#if 0 /* reserved */ +static inline void vfsub_touch_atime(struct vfsmount *h_mnt, + struct dentry *h_dentry) +{ + struct path h_path = { + .dentry = h_dentry, + .mnt = h_mnt + }; + touch_atime(&h_path); + vfsub_update_h_iattr(&h_path, /*did*/NULL); /*ignore*/ +} +#endif + +static inline int vfsub_update_time(struct inode *h_inode, + struct timespec64 *ts, int flags) +{ + return inode_update_time(h_inode, ts, flags); + /* no vfsub_update_h_iattr() since we don't have struct path */ +} + +#ifdef CONFIG_FS_POSIX_ACL +static inline int vfsub_acl_chmod(struct inode *h_inode, umode_t h_mode) +{ + int err; + + err = posix_acl_chmod(h_inode, h_mode); + if (err == -EOPNOTSUPP) + err = 0; + return err; +} +#else +AuStubInt0(vfsub_acl_chmod, struct inode *h_inode, umode_t h_mode); +#endif + +long vfsub_splice_to(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags); +long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, + loff_t *ppos, size_t len, unsigned int flags); + +static inline long vfsub_truncate(struct path *path, loff_t length) +{ + long err; + + lockdep_off(); + err = vfs_truncate(path, length); + lockdep_on(); + return err; +} + +int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr, + struct file *h_file); +int vfsub_fsync(struct file *file, struct path *path, int datasync); + +/* + * re-use branch fs's ioctl(FICLONE) while aufs itself doesn't support such + * ioctl. + */ +static inline loff_t vfsub_clone_file_range(struct file *src, struct file *dst, + loff_t len) +{ + loff_t err; + + lockdep_off(); + err = vfs_clone_file_range(src, 0, dst, 0, len, /*remap_flags*/0); + lockdep_on(); + + return err; +} + +/* copy_file_range(2) is a systemcall */ +static inline ssize_t vfsub_copy_file_range(struct file *src, loff_t src_pos, + struct file *dst, loff_t dst_pos, + size_t len, unsigned int flags) +{ + ssize_t ssz; + + lockdep_off(); + ssz = vfs_copy_file_range(src, src_pos, dst, dst_pos, len, flags); + lockdep_on(); + + return ssz; +} + +/* ---------------------------------------------------------------------- */ + +static inline loff_t vfsub_llseek(struct file *file, loff_t offset, int origin) +{ + loff_t err; + + lockdep_off(); + err = vfs_llseek(file, offset, origin); + lockdep_on(); + return err; +} + +/* ---------------------------------------------------------------------- */ + +int vfsub_sio_mkdir(struct inode *dir, struct path *path, int mode); +int vfsub_sio_rmdir(struct inode *dir, struct path *path); +int vfsub_sio_notify_change(struct path *path, struct iattr *ia, + struct inode **delegated_inode); +int vfsub_notify_change(struct path *path, struct iattr *ia, + struct inode **delegated_inode); +int vfsub_unlink(struct inode *dir, struct path *path, + struct inode **delegated_inode, int force); + +static inline int vfsub_getattr(const struct path *path, struct kstat *st) +{ + return vfs_getattr(path, st, STATX_BASIC_STATS, AT_STATX_SYNC_AS_STAT); +} + +/* ---------------------------------------------------------------------- */ + +static inline int vfsub_setxattr(struct dentry *dentry, const char *name, + const void *value, size_t size, int flags) +{ + int err; + + lockdep_off(); + err = vfs_setxattr(dentry, name, value, size, flags); + lockdep_on(); + + return err; +} + +static inline int vfsub_removexattr(struct dentry *dentry, const char *name) +{ + int err; + + lockdep_off(); + err = vfs_removexattr(dentry, name); + lockdep_on(); + + return err; +} + +#endif /* __KERNEL__ */ +#endif /* __AUFS_VFSUB_H__ */ diff --git a/fs/aufs/wbr_policy.c b/fs/aufs/wbr_policy.c new file mode 100644 index 0000000000000..309ae56b609b9 --- /dev/null +++ b/fs/aufs/wbr_policy.c @@ -0,0 +1,830 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * policies for selecting one among multiple writable branches + */ + +#include +#include "aufs.h" + +/* subset of cpup_attr() */ +static noinline_for_stack +int au_cpdown_attr(struct path *h_path, struct dentry *h_src) +{ + int err, sbits; + struct iattr ia; + struct inode *h_isrc; + + h_isrc = d_inode(h_src); + ia.ia_valid = ATTR_FORCE | ATTR_MODE | ATTR_UID | ATTR_GID; + ia.ia_mode = h_isrc->i_mode; + ia.ia_uid = h_isrc->i_uid; + ia.ia_gid = h_isrc->i_gid; + sbits = !!(ia.ia_mode & (S_ISUID | S_ISGID)); + au_cpup_attr_flags(d_inode(h_path->dentry), h_isrc->i_flags); + /* no delegation since it is just created */ + err = vfsub_sio_notify_change(h_path, &ia, /*delegated*/NULL); + + /* is this nfs only? */ + if (!err && sbits && au_test_nfs(h_path->dentry->d_sb)) { + ia.ia_valid = ATTR_FORCE | ATTR_MODE; + ia.ia_mode = h_isrc->i_mode; + err = vfsub_sio_notify_change(h_path, &ia, /*delegated*/NULL); + } + + return err; +} + +#define AuCpdown_PARENT_OPQ 1 +#define AuCpdown_WHED (1 << 1) +#define AuCpdown_MADE_DIR (1 << 2) +#define AuCpdown_DIROPQ (1 << 3) +#define au_ftest_cpdown(flags, name) ((flags) & AuCpdown_##name) +#define au_fset_cpdown(flags, name) \ + do { (flags) |= AuCpdown_##name; } while (0) +#define au_fclr_cpdown(flags, name) \ + do { (flags) &= ~AuCpdown_##name; } while (0) + +static int au_cpdown_dir_opq(struct dentry *dentry, aufs_bindex_t bdst, + unsigned int *flags) +{ + int err; + struct dentry *opq_dentry; + + opq_dentry = au_diropq_create(dentry, bdst); + err = PTR_ERR(opq_dentry); + if (IS_ERR(opq_dentry)) + goto out; + dput(opq_dentry); + au_fset_cpdown(*flags, DIROPQ); + +out: + return err; +} + +static int au_cpdown_dir_wh(struct dentry *dentry, struct dentry *h_parent, + struct inode *dir, aufs_bindex_t bdst) +{ + int err; + struct path h_path; + struct au_branch *br; + + br = au_sbr(dentry->d_sb, bdst); + h_path.dentry = au_wh_lkup(h_parent, &dentry->d_name, br); + err = PTR_ERR(h_path.dentry); + if (IS_ERR(h_path.dentry)) + goto out; + + err = 0; + if (d_is_positive(h_path.dentry)) { + h_path.mnt = au_br_mnt(br); + err = au_wh_unlink_dentry(au_h_iptr(dir, bdst), &h_path, + dentry); + } + dput(h_path.dentry); + +out: + return err; +} + +static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst, + struct au_pin *pin, + struct dentry *h_parent, void *arg) +{ + int err, rerr; + aufs_bindex_t bopq, btop; + struct path h_path; + struct dentry *parent; + struct inode *h_dir, *h_inode, *inode, *dir; + unsigned int *flags = arg; + + btop = au_dbtop(dentry); + /* dentry is di-locked */ + parent = dget_parent(dentry); + dir = d_inode(parent); + h_dir = d_inode(h_parent); + AuDebugOn(h_dir != au_h_iptr(dir, bdst)); + IMustLock(h_dir); + + err = au_lkup_neg(dentry, bdst, /*wh*/0); + if (unlikely(err < 0)) + goto out; + h_path.dentry = au_h_dptr(dentry, bdst); + h_path.mnt = au_sbr_mnt(dentry->d_sb, bdst); + err = vfsub_sio_mkdir(au_h_iptr(dir, bdst), &h_path, 0755); + if (unlikely(err)) + goto out_put; + au_fset_cpdown(*flags, MADE_DIR); + + bopq = au_dbdiropq(dentry); + au_fclr_cpdown(*flags, WHED); + au_fclr_cpdown(*flags, DIROPQ); + if (au_dbwh(dentry) == bdst) + au_fset_cpdown(*flags, WHED); + if (!au_ftest_cpdown(*flags, PARENT_OPQ) && bopq <= bdst) + au_fset_cpdown(*flags, PARENT_OPQ); + h_inode = d_inode(h_path.dentry); + inode_lock_nested(h_inode, AuLsc_I_CHILD); + if (au_ftest_cpdown(*flags, WHED)) { + err = au_cpdown_dir_opq(dentry, bdst, flags); + if (unlikely(err)) { + inode_unlock(h_inode); + goto out_dir; + } + } + + err = au_cpdown_attr(&h_path, au_h_dptr(dentry, btop)); + inode_unlock(h_inode); + if (unlikely(err)) + goto out_opq; + + if (au_ftest_cpdown(*flags, WHED)) { + err = au_cpdown_dir_wh(dentry, h_parent, dir, bdst); + if (unlikely(err)) + goto out_opq; + } + + inode = d_inode(dentry); + if (au_ibbot(inode) < bdst) + au_set_ibbot(inode, bdst); + au_set_h_iptr(inode, bdst, au_igrab(h_inode), + au_hi_flags(inode, /*isdir*/1)); + au_fhsm_wrote(dentry->d_sb, bdst, /*force*/0); + goto out; /* success */ + + /* revert */ +out_opq: + if (au_ftest_cpdown(*flags, DIROPQ)) { + inode_lock_nested(h_inode, AuLsc_I_CHILD); + rerr = au_diropq_remove(dentry, bdst); + inode_unlock(h_inode); + if (unlikely(rerr)) { + AuIOErr("failed removing diropq for %pd b%d (%d)\n", + dentry, bdst, rerr); + err = -EIO; + goto out; + } + } +out_dir: + if (au_ftest_cpdown(*flags, MADE_DIR)) { + rerr = vfsub_sio_rmdir(au_h_iptr(dir, bdst), &h_path); + if (unlikely(rerr)) { + AuIOErr("failed removing %pd b%d (%d)\n", + dentry, bdst, rerr); + err = -EIO; + } + } +out_put: + au_set_h_dptr(dentry, bdst, NULL); + if (au_dbbot(dentry) == bdst) + au_update_dbbot(dentry); +out: + dput(parent); + return err; +} + +int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst) +{ + int err; + unsigned int flags; + + flags = 0; + err = au_cp_dirs(dentry, bdst, au_cpdown_dir, &flags); + + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* policies for create */ + +int au_wbr_nonopq(struct dentry *dentry, aufs_bindex_t bindex) +{ + int err, i, j, ndentry; + aufs_bindex_t bopq; + struct au_dcsub_pages dpages; + struct au_dpage *dpage; + struct dentry **dentries, *parent, *d; + + err = au_dpages_init(&dpages, GFP_NOFS); + if (unlikely(err)) + goto out; + parent = dget_parent(dentry); + err = au_dcsub_pages_rev_aufs(&dpages, parent, /*do_include*/0); + if (unlikely(err)) + goto out_free; + + err = bindex; + for (i = 0; i < dpages.ndpage; i++) { + dpage = dpages.dpages + i; + dentries = dpage->dentries; + ndentry = dpage->ndentry; + for (j = 0; j < ndentry; j++) { + d = dentries[j]; + di_read_lock_parent2(d, !AuLock_IR); + bopq = au_dbdiropq(d); + di_read_unlock(d, !AuLock_IR); + if (bopq >= 0 && bopq < err) + err = bopq; + } + } + +out_free: + dput(parent); + au_dpages_free(&dpages); +out: + return err; +} + +static int au_wbr_bu(struct super_block *sb, aufs_bindex_t bindex) +{ + for (; bindex >= 0; bindex--) + if (!au_br_rdonly(au_sbr(sb, bindex))) + return bindex; + return -EROFS; +} + +/* top down parent */ +static int au_wbr_create_tdp(struct dentry *dentry, + unsigned int flags __maybe_unused) +{ + int err; + aufs_bindex_t btop, bindex; + struct super_block *sb; + struct dentry *parent, *h_parent; + + sb = dentry->d_sb; + btop = au_dbtop(dentry); + err = btop; + if (!au_br_rdonly(au_sbr(sb, btop))) + goto out; + + err = -EROFS; + parent = dget_parent(dentry); + for (bindex = au_dbtop(parent); bindex < btop; bindex++) { + h_parent = au_h_dptr(parent, bindex); + if (!h_parent || d_is_negative(h_parent)) + continue; + + if (!au_br_rdonly(au_sbr(sb, bindex))) { + err = bindex; + break; + } + } + dput(parent); + + /* bottom up here */ + if (unlikely(err < 0)) { + err = au_wbr_bu(sb, btop - 1); + if (err >= 0) + err = au_wbr_nonopq(dentry, err); + } + +out: + AuDbg("b%d\n", err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* an exception for the policy other than tdp */ +static int au_wbr_create_exp(struct dentry *dentry) +{ + int err; + aufs_bindex_t bwh, bdiropq; + struct dentry *parent; + + err = -1; + bwh = au_dbwh(dentry); + parent = dget_parent(dentry); + bdiropq = au_dbdiropq(parent); + if (bwh >= 0) { + if (bdiropq >= 0) + err = min(bdiropq, bwh); + else + err = bwh; + AuDbg("%d\n", err); + } else if (bdiropq >= 0) { + err = bdiropq; + AuDbg("%d\n", err); + } + dput(parent); + + if (err >= 0) + err = au_wbr_nonopq(dentry, err); + + if (err >= 0 && au_br_rdonly(au_sbr(dentry->d_sb, err))) + err = -1; + + AuDbg("%d\n", err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* round robin */ +static int au_wbr_create_init_rr(struct super_block *sb) +{ + int err; + + err = au_wbr_bu(sb, au_sbbot(sb)); + atomic_set(&au_sbi(sb)->si_wbr_rr_next, -err); /* less important */ + /* smp_mb(); */ + + AuDbg("b%d\n", err); + return err; +} + +static int au_wbr_create_rr(struct dentry *dentry, unsigned int flags) +{ + int err, nbr; + unsigned int u; + aufs_bindex_t bindex, bbot; + struct super_block *sb; + atomic_t *next; + + err = au_wbr_create_exp(dentry); + if (err >= 0) + goto out; + + sb = dentry->d_sb; + next = &au_sbi(sb)->si_wbr_rr_next; + bbot = au_sbbot(sb); + nbr = bbot + 1; + for (bindex = 0; bindex <= bbot; bindex++) { + if (!au_ftest_wbr(flags, DIR)) { + err = atomic_dec_return(next) + 1; + /* modulo for 0 is meaningless */ + if (unlikely(!err)) + err = atomic_dec_return(next) + 1; + } else + err = atomic_read(next); + AuDbg("%d\n", err); + u = err; + err = u % nbr; + AuDbg("%d\n", err); + if (!au_br_rdonly(au_sbr(sb, err))) + break; + err = -EROFS; + } + + if (err >= 0) + err = au_wbr_nonopq(dentry, err); + +out: + AuDbg("%d\n", err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* most free space */ +static void au_mfs(struct dentry *dentry, struct dentry *parent) +{ + struct super_block *sb; + struct au_branch *br; + struct au_wbr_mfs *mfs; + struct dentry *h_parent; + aufs_bindex_t bindex, bbot; + int err; + unsigned long long b, bavail; + struct path h_path; + /* reduce the stack usage */ + struct kstatfs *st; + + st = kmalloc(sizeof(*st), GFP_NOFS); + if (unlikely(!st)) { + AuWarn1("failed updating mfs(%d), ignored\n", -ENOMEM); + return; + } + + bavail = 0; + sb = dentry->d_sb; + mfs = &au_sbi(sb)->si_wbr_mfs; + MtxMustLock(&mfs->mfs_lock); + mfs->mfs_bindex = -EROFS; + mfs->mfsrr_bytes = 0; + if (!parent) { + bindex = 0; + bbot = au_sbbot(sb); + } else { + bindex = au_dbtop(parent); + bbot = au_dbtaildir(parent); + } + + for (; bindex <= bbot; bindex++) { + if (parent) { + h_parent = au_h_dptr(parent, bindex); + if (!h_parent || d_is_negative(h_parent)) + continue; + } + br = au_sbr(sb, bindex); + if (au_br_rdonly(br)) + continue; + + /* sb->s_root for NFS is unreliable */ + h_path.mnt = au_br_mnt(br); + h_path.dentry = h_path.mnt->mnt_root; + err = vfs_statfs(&h_path, st); + if (unlikely(err)) { + AuWarn1("failed statfs, b%d, %d\n", bindex, err); + continue; + } + + /* when the available size is equal, select the lower one */ + BUILD_BUG_ON(sizeof(b) < sizeof(st->f_bavail) + || sizeof(b) < sizeof(st->f_bsize)); + b = st->f_bavail * st->f_bsize; + br->br_wbr->wbr_bytes = b; + if (b >= bavail) { + bavail = b; + mfs->mfs_bindex = bindex; + mfs->mfs_jiffy = jiffies; + } + } + + mfs->mfsrr_bytes = bavail; + AuDbg("b%d\n", mfs->mfs_bindex); + au_kfree_rcu(st); +} + +static int au_wbr_create_mfs(struct dentry *dentry, unsigned int flags) +{ + int err; + struct dentry *parent; + struct super_block *sb; + struct au_wbr_mfs *mfs; + + err = au_wbr_create_exp(dentry); + if (err >= 0) + goto out; + + sb = dentry->d_sb; + parent = NULL; + if (au_ftest_wbr(flags, PARENT)) + parent = dget_parent(dentry); + mfs = &au_sbi(sb)->si_wbr_mfs; + mutex_lock(&mfs->mfs_lock); + if (time_after(jiffies, mfs->mfs_jiffy + mfs->mfs_expire) + || mfs->mfs_bindex < 0 + || au_br_rdonly(au_sbr(sb, mfs->mfs_bindex))) + au_mfs(dentry, parent); + mutex_unlock(&mfs->mfs_lock); + err = mfs->mfs_bindex; + dput(parent); + + if (err >= 0) + err = au_wbr_nonopq(dentry, err); + +out: + AuDbg("b%d\n", err); + return err; +} + +static int au_wbr_create_init_mfs(struct super_block *sb) +{ + struct au_wbr_mfs *mfs; + + mfs = &au_sbi(sb)->si_wbr_mfs; + mutex_init(&mfs->mfs_lock); + mfs->mfs_jiffy = 0; + mfs->mfs_bindex = -EROFS; + + return 0; +} + +static int au_wbr_create_fin_mfs(struct super_block *sb __maybe_unused) +{ + mutex_destroy(&au_sbi(sb)->si_wbr_mfs.mfs_lock); + return 0; +} + +/* ---------------------------------------------------------------------- */ + +/* top down regardless parent, and then mfs */ +static int au_wbr_create_tdmfs(struct dentry *dentry, + unsigned int flags __maybe_unused) +{ + int err; + aufs_bindex_t bwh, btail, bindex, bfound, bmfs; + unsigned long long watermark; + struct super_block *sb; + struct au_wbr_mfs *mfs; + struct au_branch *br; + struct dentry *parent; + + sb = dentry->d_sb; + mfs = &au_sbi(sb)->si_wbr_mfs; + mutex_lock(&mfs->mfs_lock); + if (time_after(jiffies, mfs->mfs_jiffy + mfs->mfs_expire) + || mfs->mfs_bindex < 0) + au_mfs(dentry, /*parent*/NULL); + watermark = mfs->mfsrr_watermark; + bmfs = mfs->mfs_bindex; + mutex_unlock(&mfs->mfs_lock); + + /* another style of au_wbr_create_exp() */ + bwh = au_dbwh(dentry); + parent = dget_parent(dentry); + btail = au_dbtaildir(parent); + if (bwh >= 0 && bwh < btail) + btail = bwh; + + err = au_wbr_nonopq(dentry, btail); + if (unlikely(err < 0)) + goto out; + btail = err; + bfound = -1; + for (bindex = 0; bindex <= btail; bindex++) { + br = au_sbr(sb, bindex); + if (au_br_rdonly(br)) + continue; + if (br->br_wbr->wbr_bytes > watermark) { + bfound = bindex; + break; + } + } + err = bfound; + if (err < 0) + err = bmfs; + +out: + dput(parent); + AuDbg("b%d\n", err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* most free space and then round robin */ +static int au_wbr_create_mfsrr(struct dentry *dentry, unsigned int flags) +{ + int err; + struct au_wbr_mfs *mfs; + + err = au_wbr_create_mfs(dentry, flags); + if (err >= 0) { + mfs = &au_sbi(dentry->d_sb)->si_wbr_mfs; + mutex_lock(&mfs->mfs_lock); + if (mfs->mfsrr_bytes < mfs->mfsrr_watermark) + err = au_wbr_create_rr(dentry, flags); + mutex_unlock(&mfs->mfs_lock); + } + + AuDbg("b%d\n", err); + return err; +} + +static int au_wbr_create_init_mfsrr(struct super_block *sb) +{ + int err; + + au_wbr_create_init_mfs(sb); /* ignore */ + err = au_wbr_create_init_rr(sb); + + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* top down parent and most free space */ +static int au_wbr_create_pmfs(struct dentry *dentry, unsigned int flags) +{ + int err, e2; + unsigned long long b; + aufs_bindex_t bindex, btop, bbot; + struct super_block *sb; + struct dentry *parent, *h_parent; + struct au_branch *br; + + err = au_wbr_create_tdp(dentry, flags); + if (unlikely(err < 0)) + goto out; + parent = dget_parent(dentry); + btop = au_dbtop(parent); + bbot = au_dbtaildir(parent); + if (btop == bbot) + goto out_parent; /* success */ + + e2 = au_wbr_create_mfs(dentry, flags); + if (e2 < 0) + goto out_parent; /* success */ + + /* when the available size is equal, select upper one */ + sb = dentry->d_sb; + br = au_sbr(sb, err); + b = br->br_wbr->wbr_bytes; + AuDbg("b%d, %llu\n", err, b); + + for (bindex = btop; bindex <= bbot; bindex++) { + h_parent = au_h_dptr(parent, bindex); + if (!h_parent || d_is_negative(h_parent)) + continue; + + br = au_sbr(sb, bindex); + if (!au_br_rdonly(br) && br->br_wbr->wbr_bytes > b) { + b = br->br_wbr->wbr_bytes; + err = bindex; + AuDbg("b%d, %llu\n", err, b); + } + } + + if (err >= 0) + err = au_wbr_nonopq(dentry, err); + +out_parent: + dput(parent); +out: + AuDbg("b%d\n", err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* + * - top down parent + * - most free space with parent + * - most free space round-robin regardless parent + */ +static int au_wbr_create_pmfsrr(struct dentry *dentry, unsigned int flags) +{ + int err; + unsigned long long watermark; + struct super_block *sb; + struct au_branch *br; + struct au_wbr_mfs *mfs; + + err = au_wbr_create_pmfs(dentry, flags | AuWbr_PARENT); + if (unlikely(err < 0)) + goto out; + + sb = dentry->d_sb; + br = au_sbr(sb, err); + mfs = &au_sbi(sb)->si_wbr_mfs; + mutex_lock(&mfs->mfs_lock); + watermark = mfs->mfsrr_watermark; + mutex_unlock(&mfs->mfs_lock); + if (br->br_wbr->wbr_bytes < watermark) + /* regardless the parent dir */ + err = au_wbr_create_mfsrr(dentry, flags); + +out: + AuDbg("b%d\n", err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* policies for copyup */ + +/* top down parent */ +static int au_wbr_copyup_tdp(struct dentry *dentry) +{ + return au_wbr_create_tdp(dentry, /*flags, anything is ok*/0); +} + +/* bottom up parent */ +static int au_wbr_copyup_bup(struct dentry *dentry) +{ + int err; + aufs_bindex_t bindex, btop; + struct dentry *parent, *h_parent; + struct super_block *sb; + + err = -EROFS; + sb = dentry->d_sb; + parent = dget_parent(dentry); + btop = au_dbtop(parent); + for (bindex = au_dbtop(dentry); bindex >= btop; bindex--) { + h_parent = au_h_dptr(parent, bindex); + if (!h_parent || d_is_negative(h_parent)) + continue; + + if (!au_br_rdonly(au_sbr(sb, bindex))) { + err = bindex; + break; + } + } + dput(parent); + + /* bottom up here */ + if (unlikely(err < 0)) + err = au_wbr_bu(sb, btop - 1); + + AuDbg("b%d\n", err); + return err; +} + +/* bottom up */ +int au_wbr_do_copyup_bu(struct dentry *dentry, aufs_bindex_t btop) +{ + int err; + + err = au_wbr_bu(dentry->d_sb, btop); + AuDbg("b%d\n", err); + if (err > btop) + err = au_wbr_nonopq(dentry, err); + + AuDbg("b%d\n", err); + return err; +} + +static int au_wbr_copyup_bu(struct dentry *dentry) +{ + int err; + aufs_bindex_t btop; + + btop = au_dbtop(dentry); + err = au_wbr_do_copyup_bu(dentry, btop); + return err; +} + +/* ---------------------------------------------------------------------- */ + +struct au_wbr_copyup_operations au_wbr_copyup_ops[] = { + [AuWbrCopyup_TDP] = { + .copyup = au_wbr_copyup_tdp + }, + [AuWbrCopyup_BUP] = { + .copyup = au_wbr_copyup_bup + }, + [AuWbrCopyup_BU] = { + .copyup = au_wbr_copyup_bu + } +}; + +struct au_wbr_create_operations au_wbr_create_ops[] = { + [AuWbrCreate_TDP] = { + .create = au_wbr_create_tdp + }, + [AuWbrCreate_RR] = { + .create = au_wbr_create_rr, + .init = au_wbr_create_init_rr + }, + [AuWbrCreate_MFS] = { + .create = au_wbr_create_mfs, + .init = au_wbr_create_init_mfs, + .fin = au_wbr_create_fin_mfs + }, + [AuWbrCreate_MFSV] = { + .create = au_wbr_create_mfs, + .init = au_wbr_create_init_mfs, + .fin = au_wbr_create_fin_mfs + }, + [AuWbrCreate_MFSRR] = { + .create = au_wbr_create_mfsrr, + .init = au_wbr_create_init_mfsrr, + .fin = au_wbr_create_fin_mfs + }, + [AuWbrCreate_MFSRRV] = { + .create = au_wbr_create_mfsrr, + .init = au_wbr_create_init_mfsrr, + .fin = au_wbr_create_fin_mfs + }, + [AuWbrCreate_TDMFS] = { + .create = au_wbr_create_tdmfs, + .init = au_wbr_create_init_mfs, + .fin = au_wbr_create_fin_mfs + }, + [AuWbrCreate_TDMFSV] = { + .create = au_wbr_create_tdmfs, + .init = au_wbr_create_init_mfs, + .fin = au_wbr_create_fin_mfs + }, + [AuWbrCreate_PMFS] = { + .create = au_wbr_create_pmfs, + .init = au_wbr_create_init_mfs, + .fin = au_wbr_create_fin_mfs + }, + [AuWbrCreate_PMFSV] = { + .create = au_wbr_create_pmfs, + .init = au_wbr_create_init_mfs, + .fin = au_wbr_create_fin_mfs + }, + [AuWbrCreate_PMFSRR] = { + .create = au_wbr_create_pmfsrr, + .init = au_wbr_create_init_mfsrr, + .fin = au_wbr_create_fin_mfs + }, + [AuWbrCreate_PMFSRRV] = { + .create = au_wbr_create_pmfsrr, + .init = au_wbr_create_init_mfsrr, + .fin = au_wbr_create_fin_mfs + } +}; diff --git a/fs/aufs/whout.c b/fs/aufs/whout.c new file mode 100644 index 0000000000000..bc73ca1e4bb93 --- /dev/null +++ b/fs/aufs/whout.c @@ -0,0 +1,1064 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * whiteout for logical deletion and opaque directory + */ + +#include "aufs.h" + +#define WH_MASK 0444 + +/* + * If a directory contains this file, then it is opaque. We start with the + * .wh. flag so that it is blocked by lookup. + */ +static struct qstr diropq_name = QSTR_INIT(AUFS_WH_DIROPQ, + sizeof(AUFS_WH_DIROPQ) - 1); + +/* + * generate whiteout name, which is NOT terminated by NULL. + * @name: original d_name.name + * @len: original d_name.len + * @wh: whiteout qstr + * returns zero when succeeds, otherwise error. + * succeeded value as wh->name should be freed by kfree(). + */ +int au_wh_name_alloc(struct qstr *wh, const struct qstr *name) +{ + char *p; + + if (unlikely(name->len > PATH_MAX - AUFS_WH_PFX_LEN)) + return -ENAMETOOLONG; + + wh->len = name->len + AUFS_WH_PFX_LEN; + p = kmalloc(wh->len, GFP_NOFS); + wh->name = p; + if (p) { + memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); + memcpy(p + AUFS_WH_PFX_LEN, name->name, name->len); + /* smp_mb(); */ + return 0; + } + return -ENOMEM; +} + +/* ---------------------------------------------------------------------- */ + +/* + * test if the @wh_name exists under @h_ppath. + * @try_sio specifies the necessary of super-io. + */ +int au_wh_test(struct path *h_ppath, struct qstr *wh_name, int try_sio) +{ + int err; + struct dentry *wh_dentry; + + if (!try_sio) + wh_dentry = vfsub_lkup_one(wh_name, h_ppath); + else + wh_dentry = au_sio_lkup_one(wh_name, h_ppath); + err = PTR_ERR(wh_dentry); + if (IS_ERR(wh_dentry)) { + if (err == -ENAMETOOLONG) + err = 0; + goto out; + } + + err = 0; + if (d_is_negative(wh_dentry)) + goto out_wh; /* success */ + + err = 1; + if (d_is_reg(wh_dentry)) + goto out_wh; /* success */ + + err = -EIO; + AuIOErr("%pd Invalid whiteout entry type 0%o.\n", + wh_dentry, d_inode(wh_dentry)->i_mode); + +out_wh: + dput(wh_dentry); +out: + return err; +} + +/* + * test if the @h_path->dentry sets opaque or not. + */ +int au_diropq_test(struct path *h_path) +{ + int err; + struct inode *h_dir; + + h_dir = d_inode(h_path->dentry); + err = au_wh_test(h_path, &diropq_name, + au_test_h_perm_sio(h_dir, MAY_EXEC)); + return err; +} + +/* + * returns a negative dentry whose name is unique and temporary. + */ +struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br, + struct qstr *prefix) +{ + struct dentry *dentry; + int i; + char defname[NAME_MAX - AUFS_MAX_NAMELEN + DNAME_INLINE_LEN + 1], + *name, *p; + /* strict atomic_t is unnecessary here */ + static unsigned short cnt; + struct qstr qs; + struct path h_ppath; + + BUILD_BUG_ON(sizeof(cnt) * 2 > AUFS_WH_TMP_LEN); + + name = defname; + qs.len = sizeof(defname) - DNAME_INLINE_LEN + prefix->len - 1; + if (unlikely(prefix->len > DNAME_INLINE_LEN)) { + dentry = ERR_PTR(-ENAMETOOLONG); + if (unlikely(qs.len > NAME_MAX)) + goto out; + dentry = ERR_PTR(-ENOMEM); + name = kmalloc(qs.len + 1, GFP_NOFS); + if (unlikely(!name)) + goto out; + } + + /* doubly whiteout-ed */ + memcpy(name, AUFS_WH_PFX AUFS_WH_PFX, AUFS_WH_PFX_LEN * 2); + p = name + AUFS_WH_PFX_LEN * 2; + memcpy(p, prefix->name, prefix->len); + p += prefix->len; + *p++ = '.'; + AuDebugOn(name + qs.len + 1 - p <= AUFS_WH_TMP_LEN); + + h_ppath.dentry = h_parent; + h_ppath.mnt = au_br_mnt(br); + qs.name = name; + for (i = 0; i < 3; i++) { + sprintf(p, "%.*x", AUFS_WH_TMP_LEN, cnt++); + dentry = au_sio_lkup_one(&qs, &h_ppath); + if (IS_ERR(dentry) || d_is_negative(dentry)) + goto out_name; + dput(dentry); + } + /* pr_warn("could not get random name\n"); */ + dentry = ERR_PTR(-EEXIST); + AuDbg("%.*s\n", AuLNPair(&qs)); + BUG(); + +out_name: + if (name != defname) + au_kfree_try_rcu(name); +out: + AuTraceErrPtr(dentry); + return dentry; +} + +/* + * rename the @h_dentry on @br to the whiteouted temporary name. + */ +int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br) +{ + int err; + struct path h_path = { + .mnt = au_br_mnt(br) + }; + struct inode *h_dir, *delegated; + struct dentry *h_parent; + + h_parent = h_dentry->d_parent; /* dir inode is locked */ + h_dir = d_inode(h_parent); + IMustLock(h_dir); + + h_path.dentry = au_whtmp_lkup(h_parent, br, &h_dentry->d_name); + err = PTR_ERR(h_path.dentry); + if (IS_ERR(h_path.dentry)) + goto out; + + /* under the same dir, no need to lock_rename() */ + delegated = NULL; + err = vfsub_rename(h_dir, h_dentry, h_dir, &h_path, &delegated, + /*flags*/0); + AuTraceErr(err); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal rename\n"); + iput(delegated); + } + dput(h_path.dentry); + +out: + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ +/* + * functions for removing a whiteout + */ + +static int do_unlink_wh(struct inode *h_dir, struct path *h_path) +{ + int err, force; + struct inode *delegated; + + /* + * forces superio when the dir has a sticky bit. + * this may be a violation of unix fs semantics. + */ + force = (h_dir->i_mode & S_ISVTX) + && !uid_eq(current_fsuid(), d_inode(h_path->dentry)->i_uid); + delegated = NULL; + err = vfsub_unlink(h_dir, h_path, &delegated, force); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal unlink\n"); + iput(delegated); + } + return err; +} + +int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path, + struct dentry *dentry) +{ + int err; + + err = do_unlink_wh(h_dir, h_path); + if (!err && dentry) + au_set_dbwh(dentry, -1); + + return err; +} + +static int unlink_wh_name(struct path *h_ppath, struct qstr *wh) +{ + int err; + struct path h_path; + + err = 0; + h_path.dentry = vfsub_lkup_one(wh, h_ppath); + if (IS_ERR(h_path.dentry)) + err = PTR_ERR(h_path.dentry); + else { + if (d_is_reg(h_path.dentry)) { + h_path.mnt = h_ppath->mnt; + err = do_unlink_wh(d_inode(h_ppath->dentry), &h_path); + } + dput(h_path.dentry); + } + + return err; +} + +/* ---------------------------------------------------------------------- */ +/* + * initialize/clean whiteout for a branch + */ + +static void au_wh_clean(struct inode *h_dir, struct path *whpath, + const int isdir) +{ + int err; + struct inode *delegated; + + if (d_is_negative(whpath->dentry)) + return; + + if (isdir) + err = vfsub_rmdir(h_dir, whpath); + else { + delegated = NULL; + err = vfsub_unlink(h_dir, whpath, &delegated, /*force*/0); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal unlink\n"); + iput(delegated); + } + } + if (unlikely(err)) + pr_warn("failed removing %pd (%d), ignored.\n", + whpath->dentry, err); +} + +static int test_linkable(struct dentry *h_root) +{ + struct inode *h_dir = d_inode(h_root); + + if (h_dir->i_op->link) + return 0; + + pr_err("%pd (%s) doesn't support link(2), use noplink and rw+nolwh\n", + h_root, au_sbtype(h_root->d_sb)); + return -ENOSYS; /* the branch doesn't have its ->link() */ +} + +/* todo: should this mkdir be done in /sbin/mount.aufs helper? */ +static int au_whdir(struct inode *h_dir, struct path *path) +{ + int err; + + err = -EEXIST; + if (d_is_negative(path->dentry)) { + int mode = 0700; + + if (au_test_nfs(path->dentry->d_sb)) + mode |= 0111; + err = vfsub_mkdir(h_dir, path, mode); + } else if (d_is_dir(path->dentry)) + err = 0; + else + pr_err("unknown %pd exists\n", path->dentry); + + return err; +} + +struct au_wh_base { + const struct qstr *name; + struct dentry *dentry; +}; + +static void au_wh_init_ro(struct inode *h_dir, struct au_wh_base base[], + struct path *h_path) +{ + h_path->dentry = base[AuBrWh_BASE].dentry; + au_wh_clean(h_dir, h_path, /*isdir*/0); + h_path->dentry = base[AuBrWh_PLINK].dentry; + au_wh_clean(h_dir, h_path, /*isdir*/1); + h_path->dentry = base[AuBrWh_ORPH].dentry; + au_wh_clean(h_dir, h_path, /*isdir*/1); +} + +/* + * returns tri-state, + * minus: error, caller should print the message + * zero: success + * plus: error, caller should NOT print the message + */ +static int au_wh_init_rw_nolink(struct dentry *h_root, struct au_wbr *wbr, + int do_plink, struct au_wh_base base[], + struct path *h_path) +{ + int err; + struct inode *h_dir; + + h_dir = d_inode(h_root); + h_path->dentry = base[AuBrWh_BASE].dentry; + au_wh_clean(h_dir, h_path, /*isdir*/0); + h_path->dentry = base[AuBrWh_PLINK].dentry; + if (do_plink) { + err = test_linkable(h_root); + if (unlikely(err)) { + err = 1; + goto out; + } + + err = au_whdir(h_dir, h_path); + if (unlikely(err)) + goto out; + wbr->wbr_plink = dget(base[AuBrWh_PLINK].dentry); + } else + au_wh_clean(h_dir, h_path, /*isdir*/1); + h_path->dentry = base[AuBrWh_ORPH].dentry; + err = au_whdir(h_dir, h_path); + if (unlikely(err)) + goto out; + wbr->wbr_orph = dget(base[AuBrWh_ORPH].dentry); + +out: + return err; +} + +/* + * for the moment, aufs supports the branch filesystem which does not support + * link(2). testing on FAT which does not support i_op->setattr() fully either, + * copyup failed. finally, such filesystem will not be used as the writable + * branch. + * + * returns tri-state, see above. + */ +static int au_wh_init_rw(struct dentry *h_root, struct au_wbr *wbr, + int do_plink, struct au_wh_base base[], + struct path *h_path) +{ + int err; + struct inode *h_dir; + + WbrWhMustWriteLock(wbr); + + err = test_linkable(h_root); + if (unlikely(err)) { + err = 1; + goto out; + } + + /* + * todo: should this create be done in /sbin/mount.aufs helper? + */ + err = -EEXIST; + h_dir = d_inode(h_root); + if (d_is_negative(base[AuBrWh_BASE].dentry)) { + h_path->dentry = base[AuBrWh_BASE].dentry; + err = vfsub_create(h_dir, h_path, WH_MASK, /*want_excl*/true); + } else if (d_is_reg(base[AuBrWh_BASE].dentry)) + err = 0; + else + pr_err("unknown %pd2 exists\n", base[AuBrWh_BASE].dentry); + if (unlikely(err)) + goto out; + + h_path->dentry = base[AuBrWh_PLINK].dentry; + if (do_plink) { + err = au_whdir(h_dir, h_path); + if (unlikely(err)) + goto out; + wbr->wbr_plink = dget(base[AuBrWh_PLINK].dentry); + } else + au_wh_clean(h_dir, h_path, /*isdir*/1); + wbr->wbr_whbase = dget(base[AuBrWh_BASE].dentry); + + h_path->dentry = base[AuBrWh_ORPH].dentry; + err = au_whdir(h_dir, h_path); + if (unlikely(err)) + goto out; + wbr->wbr_orph = dget(base[AuBrWh_ORPH].dentry); + +out: + return err; +} + +/* + * initialize the whiteout base file/dir for @br. + */ +int au_wh_init(struct au_branch *br, struct super_block *sb) +{ + int err, i; + const unsigned char do_plink + = !!au_opt_test(au_mntflags(sb), PLINK); + struct inode *h_dir; + struct path path = br->br_path; + struct dentry *h_root = path.dentry; + struct au_wbr *wbr = br->br_wbr; + static const struct qstr base_name[] = { + [AuBrWh_BASE] = QSTR_INIT(AUFS_BASE_NAME, + sizeof(AUFS_BASE_NAME) - 1), + [AuBrWh_PLINK] = QSTR_INIT(AUFS_PLINKDIR_NAME, + sizeof(AUFS_PLINKDIR_NAME) - 1), + [AuBrWh_ORPH] = QSTR_INIT(AUFS_ORPHDIR_NAME, + sizeof(AUFS_ORPHDIR_NAME) - 1) + }; + struct au_wh_base base[] = { + [AuBrWh_BASE] = { + .name = base_name + AuBrWh_BASE, + .dentry = NULL + }, + [AuBrWh_PLINK] = { + .name = base_name + AuBrWh_PLINK, + .dentry = NULL + }, + [AuBrWh_ORPH] = { + .name = base_name + AuBrWh_ORPH, + .dentry = NULL + } + }; + + if (wbr) + WbrWhMustWriteLock(wbr); + + for (i = 0; i < AuBrWh_Last; i++) { + /* doubly whiteouted */ + struct dentry *d; + + d = au_wh_lkup(h_root, (void *)base[i].name, br); + err = PTR_ERR(d); + if (IS_ERR(d)) + goto out; + + base[i].dentry = d; + AuDebugOn(wbr + && wbr->wbr_wh[i] + && wbr->wbr_wh[i] != base[i].dentry); + } + + if (wbr) + for (i = 0; i < AuBrWh_Last; i++) { + dput(wbr->wbr_wh[i]); + wbr->wbr_wh[i] = NULL; + } + + err = 0; + if (!au_br_writable(br->br_perm)) { + h_dir = d_inode(h_root); + au_wh_init_ro(h_dir, base, &path); + } else if (!au_br_wh_linkable(br->br_perm)) { + err = au_wh_init_rw_nolink(h_root, wbr, do_plink, base, &path); + if (err > 0) + goto out; + else if (err) + goto out_err; + } else { + err = au_wh_init_rw(h_root, wbr, do_plink, base, &path); + if (err > 0) + goto out; + else if (err) + goto out_err; + } + goto out; /* success */ + +out_err: + pr_err("an error(%d) on the writable branch %pd(%s)\n", + err, h_root, au_sbtype(h_root->d_sb)); +out: + for (i = 0; i < AuBrWh_Last; i++) + dput(base[i].dentry); + return err; +} + +/* ---------------------------------------------------------------------- */ +/* + * whiteouts are all hard-linked usually. + * when its link count reaches a ceiling, we create a new whiteout base + * asynchronously. + */ + +struct reinit_br_wh { + struct super_block *sb; + struct au_branch *br; +}; + +static void reinit_br_wh(void *arg) +{ + int err; + aufs_bindex_t bindex; + struct path h_path; + struct reinit_br_wh *a = arg; + struct au_wbr *wbr; + struct inode *dir, *delegated; + struct dentry *h_root; + struct au_hinode *hdir; + + err = 0; + wbr = a->br->br_wbr; + /* big aufs lock */ + si_noflush_write_lock(a->sb); + if (!au_br_writable(a->br->br_perm)) + goto out; + bindex = au_br_index(a->sb, a->br->br_id); + if (unlikely(bindex < 0)) + goto out; + + di_read_lock_parent(a->sb->s_root, AuLock_IR); + dir = d_inode(a->sb->s_root); + hdir = au_hi(dir, bindex); + h_root = au_h_dptr(a->sb->s_root, bindex); + AuDebugOn(h_root != au_br_dentry(a->br)); + + au_hn_inode_lock_nested(hdir, AuLsc_I_PARENT); + wbr_wh_write_lock(wbr); + err = au_h_verify(wbr->wbr_whbase, au_opt_udba(a->sb), hdir->hi_inode, + h_root, a->br); + if (!err) { + h_path.dentry = wbr->wbr_whbase; + h_path.mnt = au_br_mnt(a->br); + delegated = NULL; + err = vfsub_unlink(hdir->hi_inode, &h_path, &delegated, + /*force*/0); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal unlink\n"); + iput(delegated); + } + } else { + pr_warn("%pd is moved, ignored\n", wbr->wbr_whbase); + err = 0; + } + dput(wbr->wbr_whbase); + wbr->wbr_whbase = NULL; + if (!err) + err = au_wh_init(a->br, a->sb); + wbr_wh_write_unlock(wbr); + au_hn_inode_unlock(hdir); + di_read_unlock(a->sb->s_root, AuLock_IR); + if (!err) + au_fhsm_wrote(a->sb, bindex, /*force*/0); + +out: + if (wbr) + atomic_dec(&wbr->wbr_wh_running); + au_lcnt_dec(&a->br->br_count); + si_write_unlock(a->sb); + au_nwt_done(&au_sbi(a->sb)->si_nowait); + au_kfree_rcu(a); + if (unlikely(err)) + AuIOErr("err %d\n", err); +} + +static void kick_reinit_br_wh(struct super_block *sb, struct au_branch *br) +{ + int do_dec, wkq_err; + struct reinit_br_wh *arg; + + do_dec = 1; + if (atomic_inc_return(&br->br_wbr->wbr_wh_running) != 1) + goto out; + + /* ignore ENOMEM */ + arg = kmalloc(sizeof(*arg), GFP_NOFS); + if (arg) { + /* + * dec(wh_running), kfree(arg) and dec(br_count) + * in reinit function + */ + arg->sb = sb; + arg->br = br; + au_lcnt_inc(&br->br_count); + wkq_err = au_wkq_nowait(reinit_br_wh, arg, sb, /*flags*/0); + if (unlikely(wkq_err)) { + atomic_dec(&br->br_wbr->wbr_wh_running); + au_lcnt_dec(&br->br_count); + au_kfree_rcu(arg); + } + do_dec = 0; + } + +out: + if (do_dec) + atomic_dec(&br->br_wbr->wbr_wh_running); +} + +/* ---------------------------------------------------------------------- */ + +/* + * create the whiteout @wh. + */ +static int link_or_create_wh(struct super_block *sb, aufs_bindex_t bindex, + struct dentry *wh) +{ + int err; + struct path h_path = { + .dentry = wh + }; + struct au_branch *br; + struct au_wbr *wbr; + struct dentry *h_parent; + struct inode *h_dir, *delegated; + + h_parent = wh->d_parent; /* dir inode is locked */ + h_dir = d_inode(h_parent); + IMustLock(h_dir); + + br = au_sbr(sb, bindex); + h_path.mnt = au_br_mnt(br); + wbr = br->br_wbr; + wbr_wh_read_lock(wbr); + if (wbr->wbr_whbase) { + delegated = NULL; + err = vfsub_link(wbr->wbr_whbase, h_dir, &h_path, &delegated); + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal link\n"); + iput(delegated); + } + if (!err || err != -EMLINK) + goto out; + + /* link count full. re-initialize br_whbase. */ + kick_reinit_br_wh(sb, br); + } + + /* return this error in this context */ + err = vfsub_create(h_dir, &h_path, WH_MASK, /*want_excl*/true); + if (!err) + au_fhsm_wrote(sb, bindex, /*force*/0); + +out: + wbr_wh_read_unlock(wbr); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* + * create or remove the diropq. + */ +static struct dentry *do_diropq(struct dentry *dentry, aufs_bindex_t bindex, + unsigned int flags) +{ + struct dentry *opq_dentry; + struct super_block *sb; + struct au_branch *br; + struct path h_path; + int err; + + sb = dentry->d_sb; + br = au_sbr(sb, bindex); + h_path.dentry = au_h_dptr(dentry, bindex); + h_path.mnt = au_br_mnt(br); + opq_dentry = vfsub_lkup_one(&diropq_name, &h_path); + if (IS_ERR(opq_dentry)) + goto out; + + if (au_ftest_diropq(flags, CREATE)) { + err = link_or_create_wh(sb, bindex, opq_dentry); + if (!err) { + au_set_dbdiropq(dentry, bindex); + goto out; /* success */ + } + } else { + h_path.dentry = opq_dentry; + err = do_unlink_wh(au_h_iptr(d_inode(dentry), bindex), &h_path); + if (!err) + au_set_dbdiropq(dentry, -1); + } + dput(opq_dentry); + opq_dentry = ERR_PTR(err); + +out: + return opq_dentry; +} + +struct do_diropq_args { + struct dentry **errp; + struct dentry *dentry; + aufs_bindex_t bindex; + unsigned int flags; +}; + +static void call_do_diropq(void *args) +{ + struct do_diropq_args *a = args; + *a->errp = do_diropq(a->dentry, a->bindex, a->flags); +} + +struct dentry *au_diropq_sio(struct dentry *dentry, aufs_bindex_t bindex, + unsigned int flags) +{ + struct dentry *diropq, *h_dentry; + + h_dentry = au_h_dptr(dentry, bindex); + if (!au_test_h_perm_sio(d_inode(h_dentry), MAY_EXEC | MAY_WRITE)) + diropq = do_diropq(dentry, bindex, flags); + else { + int wkq_err; + struct do_diropq_args args = { + .errp = &diropq, + .dentry = dentry, + .bindex = bindex, + .flags = flags + }; + + wkq_err = au_wkq_wait(call_do_diropq, &args); + if (unlikely(wkq_err)) + diropq = ERR_PTR(wkq_err); + } + + return diropq; +} + +/* ---------------------------------------------------------------------- */ + +/* + * lookup whiteout dentry. + * @h_parent: lower parent dentry which must exist and be locked + * @base_name: name of dentry which will be whiteouted + * returns dentry for whiteout. + */ +struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name, + struct au_branch *br) +{ + int err; + struct qstr wh_name; + struct dentry *wh_dentry; + struct path h_path; + + err = au_wh_name_alloc(&wh_name, base_name); + wh_dentry = ERR_PTR(err); + if (!err) { + h_path.dentry = h_parent; + h_path.mnt = au_br_mnt(br); + wh_dentry = vfsub_lkup_one(&wh_name, &h_path); + au_kfree_try_rcu(wh_name.name); + } + return wh_dentry; +} + +/* + * link/create a whiteout for @dentry on @bindex. + */ +struct dentry *au_wh_create(struct dentry *dentry, aufs_bindex_t bindex, + struct dentry *h_parent) +{ + struct dentry *wh_dentry; + struct super_block *sb; + int err; + + sb = dentry->d_sb; + wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, au_sbr(sb, bindex)); + if (!IS_ERR(wh_dentry) && d_is_negative(wh_dentry)) { + err = link_or_create_wh(sb, bindex, wh_dentry); + if (!err) { + au_set_dbwh(dentry, bindex); + au_fhsm_wrote(sb, bindex, /*force*/0); + } else { + dput(wh_dentry); + wh_dentry = ERR_PTR(err); + } + } + + return wh_dentry; +} + +/* ---------------------------------------------------------------------- */ + +/* Delete all whiteouts in this directory on branch bindex. */ +static int del_wh_children(struct path *h_path, struct au_nhash *whlist, + aufs_bindex_t bindex) +{ + int err; + unsigned long ul, n; + struct qstr wh_name; + char *p; + struct hlist_head *head; + struct au_vdir_wh *pos; + struct au_vdir_destr *str; + + err = -ENOMEM; + p = (void *)__get_free_page(GFP_NOFS); + wh_name.name = p; + if (unlikely(!wh_name.name)) + goto out; + + err = 0; + memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); + p += AUFS_WH_PFX_LEN; + n = whlist->nh_num; + head = whlist->nh_head; + for (ul = 0; !err && ul < n; ul++, head++) { + hlist_for_each_entry(pos, head, wh_hash) { + if (pos->wh_bindex != bindex) + continue; + + str = &pos->wh_str; + if (str->len + AUFS_WH_PFX_LEN <= PATH_MAX) { + memcpy(p, str->name, str->len); + wh_name.len = AUFS_WH_PFX_LEN + str->len; + err = unlink_wh_name(h_path, &wh_name); + if (!err) + continue; + break; + } + AuIOErr("whiteout name too long %.*s\n", + str->len, str->name); + err = -EIO; + break; + } + } + free_page((unsigned long)wh_name.name); + +out: + return err; +} + +struct del_wh_children_args { + int *errp; + struct path *h_path; + struct au_nhash *whlist; + aufs_bindex_t bindex; +}; + +static void call_del_wh_children(void *args) +{ + struct del_wh_children_args *a = args; + *a->errp = del_wh_children(a->h_path, a->whlist, a->bindex); +} + +/* ---------------------------------------------------------------------- */ + +struct au_whtmp_rmdir *au_whtmp_rmdir_alloc(struct super_block *sb, gfp_t gfp) +{ + struct au_whtmp_rmdir *whtmp; + int err; + unsigned int rdhash; + + SiMustAnyLock(sb); + + whtmp = kzalloc(sizeof(*whtmp), gfp); + if (unlikely(!whtmp)) { + whtmp = ERR_PTR(-ENOMEM); + goto out; + } + + /* no estimation for dir size */ + rdhash = au_sbi(sb)->si_rdhash; + if (!rdhash) + rdhash = AUFS_RDHASH_DEF; + err = au_nhash_alloc(&whtmp->whlist, rdhash, gfp); + if (unlikely(err)) { + au_kfree_rcu(whtmp); + whtmp = ERR_PTR(err); + } + +out: + return whtmp; +} + +void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp) +{ + if (whtmp->br) + au_lcnt_dec(&whtmp->br->br_count); + dput(whtmp->wh_dentry); + iput(whtmp->dir); + au_nhash_wh_free(&whtmp->whlist); + au_kfree_rcu(whtmp); +} + +/* + * rmdir the whiteouted temporary named dir @h_dentry. + * @whlist: whiteouted children. + */ +int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex, + struct dentry *wh_dentry, struct au_nhash *whlist) +{ + int err; + unsigned int h_nlink; + struct path wh_path; + struct inode *wh_inode, *h_dir; + struct au_branch *br; + + h_dir = d_inode(wh_dentry->d_parent); /* dir inode is locked */ + IMustLock(h_dir); + + br = au_sbr(dir->i_sb, bindex); + wh_path.dentry = wh_dentry; + wh_path.mnt = au_br_mnt(br); + wh_inode = d_inode(wh_dentry); + inode_lock_nested(wh_inode, AuLsc_I_CHILD); + + /* + * someone else might change some whiteouts while we were sleeping. + * it means this whlist may have an obsoleted entry. + */ + if (!au_test_h_perm_sio(wh_inode, MAY_EXEC | MAY_WRITE)) + err = del_wh_children(&wh_path, whlist, bindex); + else { + int wkq_err; + struct del_wh_children_args args = { + .errp = &err, + .h_path = &wh_path, + .whlist = whlist, + .bindex = bindex + }; + + wkq_err = au_wkq_wait(call_del_wh_children, &args); + if (unlikely(wkq_err)) + err = wkq_err; + } + inode_unlock(wh_inode); + + if (!err) { + h_nlink = h_dir->i_nlink; + err = vfsub_rmdir(h_dir, &wh_path); + /* some fs doesn't change the parent nlink in some cases */ + h_nlink -= h_dir->i_nlink; + } + + if (!err) { + if (au_ibtop(dir) == bindex) { + /* todo: dir->i_mutex is necessary */ + au_cpup_attr_timesizes(dir); + if (h_nlink) + vfsub_drop_nlink(dir); + } + return 0; /* success */ + } + + pr_warn("failed removing %pd(%d), ignored\n", wh_dentry, err); + return err; +} + +static void call_rmdir_whtmp(void *args) +{ + int err; + aufs_bindex_t bindex; + struct au_whtmp_rmdir *a = args; + struct super_block *sb; + struct dentry *h_parent; + struct inode *h_dir; + struct au_hinode *hdir; + + /* rmdir by nfsd may cause deadlock with this i_mutex */ + /* inode_lock(a->dir); */ + err = -EROFS; + sb = a->dir->i_sb; + si_read_lock(sb, !AuLock_FLUSH); + if (!au_br_writable(a->br->br_perm)) + goto out; + bindex = au_br_index(sb, a->br->br_id); + if (unlikely(bindex < 0)) + goto out; + + err = -EIO; + ii_write_lock_parent(a->dir); + h_parent = dget_parent(a->wh_dentry); + h_dir = d_inode(h_parent); + hdir = au_hi(a->dir, bindex); + err = vfsub_mnt_want_write(au_br_mnt(a->br)); + if (unlikely(err)) + goto out_mnt; + au_hn_inode_lock_nested(hdir, AuLsc_I_PARENT); + err = au_h_verify(a->wh_dentry, au_opt_udba(sb), h_dir, h_parent, + a->br); + if (!err) + err = au_whtmp_rmdir(a->dir, bindex, a->wh_dentry, &a->whlist); + au_hn_inode_unlock(hdir); + vfsub_mnt_drop_write(au_br_mnt(a->br)); + +out_mnt: + dput(h_parent); + ii_write_unlock(a->dir); +out: + /* inode_unlock(a->dir); */ + au_whtmp_rmdir_free(a); + si_read_unlock(sb); + au_nwt_done(&au_sbi(sb)->si_nowait); + if (unlikely(err)) + AuIOErr("err %d\n", err); +} + +void au_whtmp_kick_rmdir(struct inode *dir, aufs_bindex_t bindex, + struct dentry *wh_dentry, struct au_whtmp_rmdir *args) +{ + int wkq_err; + struct super_block *sb; + + IMustLock(dir); + + /* all post-process will be done in do_rmdir_whtmp(). */ + sb = dir->i_sb; + args->dir = au_igrab(dir); + args->br = au_sbr(sb, bindex); + au_lcnt_inc(&args->br->br_count); + args->wh_dentry = dget(wh_dentry); + wkq_err = au_wkq_nowait(call_rmdir_whtmp, args, sb, /*flags*/0); + if (unlikely(wkq_err)) { + pr_warn("rmdir error %pd (%d), ignored\n", wh_dentry, wkq_err); + au_whtmp_rmdir_free(args); + } +} diff --git a/fs/aufs/whout.h b/fs/aufs/whout.h new file mode 100644 index 0000000000000..f9020294a86e3 --- /dev/null +++ b/fs/aufs/whout.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * whiteout for logical deletion and opaque directory + */ + +#ifndef __AUFS_WHOUT_H__ +#define __AUFS_WHOUT_H__ + +#ifdef __KERNEL__ + +#include "dir.h" + +/* whout.c */ +int au_wh_name_alloc(struct qstr *wh, const struct qstr *name); +int au_wh_test(struct path *h_ppath, struct qstr *wh_name, int try_sio); +int au_diropq_test(struct path *h_path); +struct au_branch; +struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br, + struct qstr *prefix); +int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br); +int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path, + struct dentry *dentry); +int au_wh_init(struct au_branch *br, struct super_block *sb); + +/* diropq flags */ +#define AuDiropq_CREATE 1 +#define au_ftest_diropq(flags, name) ((flags) & AuDiropq_##name) +#define au_fset_diropq(flags, name) \ + do { (flags) |= AuDiropq_##name; } while (0) +#define au_fclr_diropq(flags, name) \ + do { (flags) &= ~AuDiropq_##name; } while (0) + +struct dentry *au_diropq_sio(struct dentry *dentry, aufs_bindex_t bindex, + unsigned int flags); +struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name, + struct au_branch *br); +struct dentry *au_wh_create(struct dentry *dentry, aufs_bindex_t bindex, + struct dentry *h_parent); + +/* real rmdir for the whiteout-ed dir */ +struct au_whtmp_rmdir { + struct inode *dir; + struct au_branch *br; + struct dentry *wh_dentry; + struct au_nhash whlist; +}; + +struct au_whtmp_rmdir *au_whtmp_rmdir_alloc(struct super_block *sb, gfp_t gfp); +void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp); +int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex, + struct dentry *wh_dentry, struct au_nhash *whlist); +void au_whtmp_kick_rmdir(struct inode *dir, aufs_bindex_t bindex, + struct dentry *wh_dentry, struct au_whtmp_rmdir *args); + +/* ---------------------------------------------------------------------- */ + +static inline struct dentry *au_diropq_create(struct dentry *dentry, + aufs_bindex_t bindex) +{ + return au_diropq_sio(dentry, bindex, AuDiropq_CREATE); +} + +static inline int au_diropq_remove(struct dentry *dentry, aufs_bindex_t bindex) +{ + return PTR_ERR(au_diropq_sio(dentry, bindex, !AuDiropq_CREATE)); +} + +#endif /* __KERNEL__ */ +#endif /* __AUFS_WHOUT_H__ */ diff --git a/fs/aufs/wkq.c b/fs/aufs/wkq.c new file mode 100644 index 0000000000000..12b306f185d9f --- /dev/null +++ b/fs/aufs/wkq.c @@ -0,0 +1,372 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * workqueue for asynchronous/super-io operations + * todo: try new credential scheme + */ + +#include +#include "aufs.h" + +/* internal workqueue named AUFS_WKQ_NAME */ + +static struct workqueue_struct *au_wkq; + +struct au_wkinfo { + struct work_struct wk; + struct kobject *kobj; + + unsigned int flags; /* see wkq.h */ + + au_wkq_func_t func; + void *args; + +#ifdef CONFIG_LOCKDEP + int dont_check; + struct held_lock **hlock; +#endif + + struct completion *comp; +}; + +/* ---------------------------------------------------------------------- */ +/* + * Aufs passes some operations to the workqueue such as the internal copyup. + * This scheme looks rather unnatural for LOCKDEP debugging feature, since the + * job run by workqueue depends upon the locks acquired in the other task. + * Delegating a small operation to the workqueue, aufs passes its lockdep + * information too. And the job in the workqueue restores the info in order to + * pretend as if it acquired those locks. This is just to make LOCKDEP work + * correctly and expectedly. + */ + +#ifndef CONFIG_LOCKDEP +AuStubInt0(au_wkq_lockdep_alloc, struct au_wkinfo *wkinfo); +AuStubVoid(au_wkq_lockdep_free, struct au_wkinfo *wkinfo); +AuStubVoid(au_wkq_lockdep_pre, struct au_wkinfo *wkinfo); +AuStubVoid(au_wkq_lockdep_post, struct au_wkinfo *wkinfo); +AuStubVoid(au_wkq_lockdep_init, struct au_wkinfo *wkinfo); +#else +static void au_wkq_lockdep_init(struct au_wkinfo *wkinfo) +{ + wkinfo->hlock = NULL; + wkinfo->dont_check = 0; +} + +/* + * 1: matched + * 0: unmatched + */ +static int au_wkq_lockdep_test(struct lock_class_key *key, const char *name) +{ + static DEFINE_SPINLOCK(spin); + static struct { + char *name; + struct lock_class_key *key; + } a[] = { + { .name = "&sbinfo->si_rwsem" }, + { .name = "&finfo->fi_rwsem" }, + { .name = "&dinfo->di_rwsem" }, + { .name = "&iinfo->ii_rwsem" } + }; + static int set; + int i; + + /* lockless read from 'set.' see below */ + if (set == ARRAY_SIZE(a)) { + for (i = 0; i < ARRAY_SIZE(a); i++) + if (a[i].key == key) + goto match; + goto unmatch; + } + + spin_lock(&spin); + if (set) + for (i = 0; i < ARRAY_SIZE(a); i++) + if (a[i].key == key) { + spin_unlock(&spin); + goto match; + } + for (i = 0; i < ARRAY_SIZE(a); i++) { + if (a[i].key) { + if (unlikely(a[i].key == key)) { /* rare but possible */ + spin_unlock(&spin); + goto match; + } else + continue; + } + if (strstr(a[i].name, name)) { + /* + * the order of these three lines is important for the + * lockless read above. + */ + a[i].key = key; + spin_unlock(&spin); + set++; + /* AuDbg("%d, %s\n", set, name); */ + goto match; + } + } + spin_unlock(&spin); + goto unmatch; + +match: + return 1; +unmatch: + return 0; +} + +static int au_wkq_lockdep_alloc(struct au_wkinfo *wkinfo) +{ + int err, n; + struct task_struct *curr; + struct held_lock **hl, *held_locks, *p; + + err = 0; + curr = current; + wkinfo->dont_check = lockdep_recursing(curr); + if (wkinfo->dont_check) + goto out; + n = curr->lockdep_depth; + if (!n) + goto out; + + err = -ENOMEM; + wkinfo->hlock = kmalloc_array(n + 1, sizeof(*wkinfo->hlock), GFP_NOFS); + if (unlikely(!wkinfo->hlock)) + goto out; + + err = 0; +#if 0 /* left for debugging */ + if (0 && au_debug_test()) + lockdep_print_held_locks(curr); +#endif + held_locks = curr->held_locks; + hl = wkinfo->hlock; + while (n--) { + p = held_locks++; + if (au_wkq_lockdep_test(p->instance->key, p->instance->name)) + *hl++ = p; + } + *hl = NULL; + +out: + return err; +} + +static void au_wkq_lockdep_free(struct au_wkinfo *wkinfo) +{ + au_kfree_try_rcu(wkinfo->hlock); +} + +static void au_wkq_lockdep_pre(struct au_wkinfo *wkinfo) +{ + struct held_lock *p, **hl = wkinfo->hlock; + int subclass; + + if (wkinfo->dont_check) + lockdep_off(); + if (!hl) + return; + while ((p = *hl++)) { /* assignment */ + subclass = lockdep_hlock_class(p)->subclass; + /* AuDbg("%s, %d\n", p->instance->name, subclass); */ + if (p->read) + rwsem_acquire_read(p->instance, subclass, 0, + /*p->acquire_ip*/_RET_IP_); + else + rwsem_acquire(p->instance, subclass, 0, + /*p->acquire_ip*/_RET_IP_); + } +} + +static void au_wkq_lockdep_post(struct au_wkinfo *wkinfo) +{ + struct held_lock *p, **hl = wkinfo->hlock; + + if (wkinfo->dont_check) + lockdep_on(); + if (!hl) + return; + while ((p = *hl++)) /* assignment */ + rwsem_release(p->instance, /*p->acquire_ip*/_RET_IP_); +} +#endif + +static void wkq_func(struct work_struct *wk) +{ + struct au_wkinfo *wkinfo = container_of(wk, struct au_wkinfo, wk); + + AuDebugOn(!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)); + AuDebugOn(rlimit(RLIMIT_FSIZE) != RLIM_INFINITY); + + au_wkq_lockdep_pre(wkinfo); + wkinfo->func(wkinfo->args); + au_wkq_lockdep_post(wkinfo); + if (au_ftest_wkq(wkinfo->flags, WAIT)) + complete(wkinfo->comp); + else { + kobject_put(wkinfo->kobj); + module_put(THIS_MODULE); /* todo: ?? */ + au_kfree_rcu(wkinfo); + } +} + +/* + * Since struct completion is large, try allocating it dynamically. + */ +#define AuWkqCompDeclare(name) struct completion *comp = NULL + +static int au_wkq_comp_alloc(struct au_wkinfo *wkinfo, struct completion **comp) +{ + *comp = kmalloc(sizeof(**comp), GFP_NOFS); + if (*comp) { + init_completion(*comp); + wkinfo->comp = *comp; + return 0; + } + return -ENOMEM; +} + +static void au_wkq_comp_free(struct completion *comp) +{ + au_kfree_rcu(comp); +} + +static void au_wkq_run(struct au_wkinfo *wkinfo) +{ + if (au_ftest_wkq(wkinfo->flags, NEST)) { + if (au_wkq_test()) { + AuWarn1("wkq from wkq, unless silly-rename on NFS," + " due to a dead dir by UDBA," + " or async xino write?\n"); + AuDebugOn(au_ftest_wkq(wkinfo->flags, WAIT)); + } + } else + au_dbg_verify_kthread(); + + if (au_ftest_wkq(wkinfo->flags, WAIT)) { + INIT_WORK_ONSTACK(&wkinfo->wk, wkq_func); + queue_work(au_wkq, &wkinfo->wk); + } else { + INIT_WORK(&wkinfo->wk, wkq_func); + schedule_work(&wkinfo->wk); + } +} + +/* + * Be careful. It is easy to make deadlock happen. + * processA: lock, wkq and wait + * processB: wkq and wait, lock in wkq + * --> deadlock + */ +int au_wkq_do_wait(unsigned int flags, au_wkq_func_t func, void *args) +{ + int err; + AuWkqCompDeclare(comp); + struct au_wkinfo wkinfo = { + .flags = flags, + .func = func, + .args = args + }; + + err = au_wkq_comp_alloc(&wkinfo, &comp); + if (unlikely(err)) + goto out; + err = au_wkq_lockdep_alloc(&wkinfo); + if (unlikely(err)) + goto out_comp; + if (!err) { + au_wkq_run(&wkinfo); + /* no timeout, no interrupt */ + wait_for_completion(wkinfo.comp); + } + au_wkq_lockdep_free(&wkinfo); + +out_comp: + au_wkq_comp_free(comp); +out: + destroy_work_on_stack(&wkinfo.wk); + return err; +} + +/* + * Note: dget/dput() in func for aufs dentries are not supported. It will be a + * problem in a concurrent umounting. + */ +int au_wkq_nowait(au_wkq_func_t func, void *args, struct super_block *sb, + unsigned int flags) +{ + int err; + struct au_wkinfo *wkinfo; + + atomic_inc(&au_sbi(sb)->si_nowait.nw_len); + + /* + * wkq_func() must free this wkinfo. + * it highly depends upon the implementation of workqueue. + */ + err = 0; + wkinfo = kmalloc(sizeof(*wkinfo), GFP_NOFS); + if (wkinfo) { + wkinfo->kobj = &au_sbi(sb)->si_kobj; + wkinfo->flags = flags & ~AuWkq_WAIT; + wkinfo->func = func; + wkinfo->args = args; + wkinfo->comp = NULL; + au_wkq_lockdep_init(wkinfo); + kobject_get(wkinfo->kobj); + __module_get(THIS_MODULE); /* todo: ?? */ + + au_wkq_run(wkinfo); + } else { + err = -ENOMEM; + au_nwt_done(&au_sbi(sb)->si_nowait); + } + + return err; +} + +/* ---------------------------------------------------------------------- */ + +void au_nwt_init(struct au_nowait_tasks *nwt) +{ + atomic_set(&nwt->nw_len, 0); + /* smp_mb(); */ /* atomic_set */ + init_waitqueue_head(&nwt->nw_wq); +} + +void au_wkq_fin(void) +{ + destroy_workqueue(au_wkq); +} + +int __init au_wkq_init(void) +{ + int err; + + err = 0; + au_wkq = alloc_workqueue(AUFS_WKQ_NAME, 0, WQ_DFL_ACTIVE); + if (IS_ERR(au_wkq)) + err = PTR_ERR(au_wkq); + else if (!au_wkq) + err = -ENOMEM; + + return err; +} diff --git a/fs/aufs/wkq.h b/fs/aufs/wkq.h new file mode 100644 index 0000000000000..d7d200e607bda --- /dev/null +++ b/fs/aufs/wkq.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * workqueue for asynchronous/super-io operations + * todo: try new credentials management scheme + */ + +#ifndef __AUFS_WKQ_H__ +#define __AUFS_WKQ_H__ + +#ifdef __KERNEL__ + +#include + +struct super_block; + +/* ---------------------------------------------------------------------- */ + +/* + * in the next operation, wait for the 'nowait' tasks in system-wide workqueue + */ +struct au_nowait_tasks { + atomic_t nw_len; + wait_queue_head_t nw_wq; +}; + +/* ---------------------------------------------------------------------- */ + +typedef void (*au_wkq_func_t)(void *args); + +/* wkq flags */ +#define AuWkq_WAIT 1 +#define AuWkq_NEST (1 << 1) +#define au_ftest_wkq(flags, name) ((flags) & AuWkq_##name) +#define au_fset_wkq(flags, name) \ + do { (flags) |= AuWkq_##name; } while (0) +#define au_fclr_wkq(flags, name) \ + do { (flags) &= ~AuWkq_##name; } while (0) + +/* wkq.c */ +int au_wkq_do_wait(unsigned int flags, au_wkq_func_t func, void *args); +int au_wkq_nowait(au_wkq_func_t func, void *args, struct super_block *sb, + unsigned int flags); +void au_nwt_init(struct au_nowait_tasks *nwt); +int __init au_wkq_init(void); +void au_wkq_fin(void); + +/* ---------------------------------------------------------------------- */ + +static inline int au_wkq_test(void) +{ + return current->flags & PF_WQ_WORKER; +} + +static inline int au_wkq_wait(au_wkq_func_t func, void *args) +{ + return au_wkq_do_wait(AuWkq_WAIT, func, args); +} + +static inline void au_nwt_done(struct au_nowait_tasks *nwt) +{ + if (atomic_dec_and_test(&nwt->nw_len)) + wake_up_all(&nwt->nw_wq); +} + +static inline int au_nwt_flush(struct au_nowait_tasks *nwt) +{ + wait_event(nwt->nw_wq, !atomic_read(&nwt->nw_len)); + return 0; +} + +#endif /* __KERNEL__ */ +#endif /* __AUFS_WKQ_H__ */ diff --git a/fs/aufs/xattr.c b/fs/aufs/xattr.c new file mode 100644 index 0000000000000..7b2f62a150419 --- /dev/null +++ b/fs/aufs/xattr.c @@ -0,0 +1,356 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2014-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * handling xattr functions + */ + +#include +#include +#include +#include "aufs.h" + +static int au_xattr_ignore(int err, char *name, unsigned int ignore_flags) +{ + if (!ignore_flags) + goto out; + switch (err) { + case -ENOMEM: + case -EDQUOT: + goto out; + } + + if ((ignore_flags & AuBrAttr_ICEX) == AuBrAttr_ICEX) { + err = 0; + goto out; + } + +#define cmp(brattr, prefix) do { \ + if (!strncmp(name, XATTR_##prefix##_PREFIX, \ + XATTR_##prefix##_PREFIX_LEN)) { \ + if (ignore_flags & AuBrAttr_ICEX_##brattr) \ + err = 0; \ + goto out; \ + } \ + } while (0) + + cmp(SEC, SECURITY); + cmp(SYS, SYSTEM); + cmp(TR, TRUSTED); + cmp(USR, USER); +#undef cmp + + if (ignore_flags & AuBrAttr_ICEX_OTH) + err = 0; + +out: + return err; +} + +static const int au_xattr_out_of_list = AuBrAttr_ICEX_OTH << 1; + +static int au_do_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, + char *name, char **buf, unsigned int ignore_flags, + unsigned int verbose) +{ + int err; + ssize_t ssz; + struct inode *h_idst; + + ssz = vfs_getxattr_alloc(h_src, name, buf, 0, GFP_NOFS); + err = ssz; + if (unlikely(err <= 0)) { + if (err == -ENODATA + || (err == -EOPNOTSUPP + && ((ignore_flags & au_xattr_out_of_list) + || (au_test_nfs_noacl(d_inode(h_src)) + && (!strcmp(name, XATTR_NAME_POSIX_ACL_ACCESS) + || !strcmp(name, + XATTR_NAME_POSIX_ACL_DEFAULT)))) + )) + err = 0; + if (err && (verbose || au_debug_test())) + pr_err("%s, err %d\n", name, err); + goto out; + } + + /* unlock it temporary */ + h_idst = d_inode(h_dst); + inode_unlock(h_idst); + err = vfsub_setxattr(h_dst, name, *buf, ssz, /*flags*/0); + inode_lock_nested(h_idst, AuLsc_I_CHILD2); + if (unlikely(err)) { + if (verbose || au_debug_test()) + pr_err("%s, err %d\n", name, err); + err = au_xattr_ignore(err, name, ignore_flags); + } + +out: + return err; +} + +int au_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, int ignore_flags, + unsigned int verbose) +{ + int err, unlocked, acl_access, acl_default; + ssize_t ssz; + struct inode *h_isrc, *h_idst; + char *value, *p, *o, *e; + + /* try stopping to update the source inode while we are referencing */ + /* there should not be the parent-child relationship between them */ + h_isrc = d_inode(h_src); + h_idst = d_inode(h_dst); + inode_unlock(h_idst); + inode_lock_shared_nested(h_isrc, AuLsc_I_CHILD); + inode_lock_nested(h_idst, AuLsc_I_CHILD2); + unlocked = 0; + + /* some filesystems don't list POSIX ACL, for example tmpfs */ + ssz = vfs_listxattr(h_src, NULL, 0); + err = ssz; + if (unlikely(err < 0)) { + AuTraceErr(err); + if (err == -ENODATA + || err == -EOPNOTSUPP) + err = 0; /* ignore */ + goto out; + } + + err = 0; + p = NULL; + o = NULL; + if (ssz) { + err = -ENOMEM; + p = kmalloc(ssz, GFP_NOFS); + o = p; + if (unlikely(!p)) + goto out; + err = vfs_listxattr(h_src, p, ssz); + } + inode_unlock_shared(h_isrc); + unlocked = 1; + AuDbg("err %d, ssz %zd\n", err, ssz); + if (unlikely(err < 0)) + goto out_free; + + err = 0; + e = p + ssz; + value = NULL; + acl_access = 0; + acl_default = 0; + while (!err && p < e) { + acl_access |= !strncmp(p, XATTR_NAME_POSIX_ACL_ACCESS, + sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1); + acl_default |= !strncmp(p, XATTR_NAME_POSIX_ACL_DEFAULT, + sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) + - 1); + err = au_do_cpup_xattr(h_dst, h_src, p, &value, ignore_flags, + verbose); + p += strlen(p) + 1; + } + AuTraceErr(err); + ignore_flags |= au_xattr_out_of_list; + if (!err && !acl_access) { + err = au_do_cpup_xattr(h_dst, h_src, + XATTR_NAME_POSIX_ACL_ACCESS, &value, + ignore_flags, verbose); + AuTraceErr(err); + } + if (!err && !acl_default) { + err = au_do_cpup_xattr(h_dst, h_src, + XATTR_NAME_POSIX_ACL_DEFAULT, &value, + ignore_flags, verbose); + AuTraceErr(err); + } + + au_kfree_try_rcu(value); + +out_free: + au_kfree_try_rcu(o); +out: + if (!unlocked) + inode_unlock_shared(h_isrc); + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int au_smack_reentering(struct super_block *sb) +{ +#if IS_ENABLED(CONFIG_SECURITY_SMACK) || IS_ENABLED(CONFIG_SECURITY_SELINUX) + /* + * as a part of lookup, smack_d_instantiate() is called, and it calls + * i_op->getxattr(). ouch. + */ + return si_pid_test(sb); +#else + return 0; +#endif +} + +enum { + AU_XATTR_LIST, + AU_XATTR_GET +}; + +struct au_lgxattr { + int type; + union { + struct { + char *list; + size_t size; + } list; + struct { + const char *name; + void *value; + size_t size; + } get; + } u; +}; + +static ssize_t au_lgxattr(struct dentry *dentry, struct inode *inode, + struct au_lgxattr *arg) +{ + ssize_t err; + int reenter; + struct path h_path; + struct super_block *sb; + + sb = dentry->d_sb; + reenter = au_smack_reentering(sb); + if (!reenter) { + err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); + if (unlikely(err)) + goto out; + } + err = au_h_path_getattr(dentry, inode, /*force*/1, &h_path, reenter); + if (unlikely(err)) + goto out_si; + if (unlikely(!h_path.dentry)) + /* illegally overlapped or something */ + goto out_di; /* pretending success */ + + /* always topmost entry only */ + switch (arg->type) { + case AU_XATTR_LIST: + err = vfs_listxattr(h_path.dentry, + arg->u.list.list, arg->u.list.size); + break; + case AU_XATTR_GET: + AuDebugOn(d_is_negative(h_path.dentry)); + err = vfs_getxattr(h_path.dentry, + arg->u.get.name, arg->u.get.value, + arg->u.get.size); + break; + } + +out_di: + if (!reenter) + di_read_unlock(dentry, AuLock_IR); +out_si: + if (!reenter) + si_read_unlock(sb); +out: + AuTraceErr(err); + return err; +} + +ssize_t aufs_listxattr(struct dentry *dentry, char *list, size_t size) +{ + struct au_lgxattr arg = { + .type = AU_XATTR_LIST, + .u.list = { + .list = list, + .size = size + }, + }; + + return au_lgxattr(dentry, /*inode*/NULL, &arg); +} + +static ssize_t au_getxattr(struct dentry *dentry, struct inode *inode, + const char *name, void *value, size_t size) +{ + struct au_lgxattr arg = { + .type = AU_XATTR_GET, + .u.get = { + .name = name, + .value = value, + .size = size + }, + }; + + return au_lgxattr(dentry, inode, &arg); +} + +static int au_setxattr(struct dentry *dentry, struct inode *inode, + const char *name, const void *value, size_t size, + int flags) +{ + struct au_sxattr arg = { + .type = AU_XATTR_SET, + .u.set = { + .name = name, + .value = value, + .size = size, + .flags = flags + }, + }; + + return au_sxattr(dentry, inode, &arg); +} + +/* ---------------------------------------------------------------------- */ + +static int au_xattr_get(const struct xattr_handler *handler, + struct dentry *dentry, struct inode *inode, + const char *name, void *buffer, size_t size) +{ + return au_getxattr(dentry, inode, name, buffer, size); +} + +static int au_xattr_set(const struct xattr_handler *handler, + struct dentry *dentry, struct inode *inode, + const char *name, const void *value, size_t size, + int flags) +{ + return au_setxattr(dentry, inode, name, value, size, flags); +} + +static const struct xattr_handler au_xattr_handler = { + .name = "", + .prefix = "", + .get = au_xattr_get, + .set = au_xattr_set +}; + +static const struct xattr_handler *au_xattr_handlers[] = { +#ifdef CONFIG_FS_POSIX_ACL + &posix_acl_access_xattr_handler, + &posix_acl_default_xattr_handler, +#endif + &au_xattr_handler, /* must be last */ + NULL +}; + +void au_xattr_init(struct super_block *sb) +{ + sb->s_xattr = au_xattr_handlers; +} diff --git a/fs/aufs/xino.c b/fs/aufs/xino.c new file mode 100644 index 0000000000000..e8337fd8bd07c --- /dev/null +++ b/fs/aufs/xino.c @@ -0,0 +1,1926 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * external inode number translation table and bitmap + * + * things to consider + * - the lifetime + * + au_xino object + * + XINO files (xino, xib, xigen) + * + dynamic debugfs entries (xiN) + * + static debugfs entries (xib, xigen) + * + static sysfs entry (xi_path) + * - several entry points to handle them. + * + mount(2) without xino option (default) + * + mount(2) with xino option + * + mount(2) with noxino option + * + umount(2) + * + remount with add/del branches + * + remount with xino/noxino options + */ + +#include +#include +#include "aufs.h" + +static aufs_bindex_t sbr_find_shared(struct super_block *sb, aufs_bindex_t btop, + aufs_bindex_t bbot, + struct super_block *h_sb) +{ + /* todo: try binary-search if the branches are many */ + for (; btop <= bbot; btop++) + if (h_sb == au_sbr_sb(sb, btop)) + return btop; + return -1; +} + +/* + * find another branch who is on the same filesystem of the specified + * branch{@btgt}. search until @bbot. + */ +static aufs_bindex_t is_sb_shared(struct super_block *sb, aufs_bindex_t btgt, + aufs_bindex_t bbot) +{ + aufs_bindex_t bindex; + struct super_block *tgt_sb; + + tgt_sb = au_sbr_sb(sb, btgt); + bindex = sbr_find_shared(sb, /*btop*/0, btgt - 1, tgt_sb); + if (bindex < 0) + bindex = sbr_find_shared(sb, btgt + 1, bbot, tgt_sb); + + return bindex; +} + +/* ---------------------------------------------------------------------- */ + +/* + * stop unnecessary notify events at creating xino files + */ + +aufs_bindex_t au_xi_root(struct super_block *sb, struct dentry *dentry) +{ + aufs_bindex_t bfound, bindex, bbot; + struct dentry *parent; + struct au_branch *br; + + bfound = -1; + parent = dentry->d_parent; /* safe d_parent access */ + bbot = au_sbbot(sb); + for (bindex = 0; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + if (au_br_dentry(br) == parent) { + bfound = bindex; + break; + } + } + + AuDbg("bfound b%d\n", bfound); + return bfound; +} + +struct au_xino_lock_dir { + struct au_hinode *hdir; + struct dentry *parent; + struct inode *dir; +}; + +static struct dentry *au_dget_parent_lock(struct dentry *dentry, + unsigned int lsc) +{ + struct dentry *parent; + struct inode *dir; + + parent = dget_parent(dentry); + dir = d_inode(parent); + inode_lock_nested(dir, lsc); +#if 0 /* it should not happen */ + spin_lock(&dentry->d_lock); + if (unlikely(dentry->d_parent != parent)) { + spin_unlock(&dentry->d_lock); + inode_unlock(dir); + dput(parent); + parent = NULL; + goto out; + } + spin_unlock(&dentry->d_lock); + +out: +#endif + return parent; +} + +static void au_xino_lock_dir(struct super_block *sb, struct path *xipath, + struct au_xino_lock_dir *ldir) +{ + aufs_bindex_t bindex; + + ldir->hdir = NULL; + bindex = au_xi_root(sb, xipath->dentry); + if (bindex >= 0) { + /* rw branch root */ + ldir->hdir = au_hi(d_inode(sb->s_root), bindex); + au_hn_inode_lock_nested(ldir->hdir, AuLsc_I_PARENT); + } else { + /* other */ + ldir->parent = au_dget_parent_lock(xipath->dentry, + AuLsc_I_PARENT); + ldir->dir = d_inode(ldir->parent); + } +} + +static void au_xino_unlock_dir(struct au_xino_lock_dir *ldir) +{ + if (ldir->hdir) + au_hn_inode_unlock(ldir->hdir); + else { + inode_unlock(ldir->dir); + dput(ldir->parent); + } +} + +/* ---------------------------------------------------------------------- */ + +/* + * create and set a new xino file + */ +struct file *au_xino_create(struct super_block *sb, char *fpath, int silent, + int wbrtop) +{ + struct file *file; + struct dentry *h_parent, *d; + struct inode *h_dir, *inode; + int err; + static DEFINE_MUTEX(mtx); + + /* + * at mount-time, and the xino file is the default path, + * hnotify is disabled so we have no notify events to ignore. + * when a user specified the xino, we cannot get au_hdir to be ignored. + */ + if (!wbrtop) + mutex_lock(&mtx); + file = vfsub_filp_open(fpath, O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE + /* | __FMODE_NONOTIFY */, + 0666); + if (IS_ERR(file)) { + if (!wbrtop) + mutex_unlock(&mtx); + if (!silent) + pr_err("open %s(%ld)\n", fpath, PTR_ERR(file)); + return file; + } + + /* keep file count */ + err = 0; + d = file->f_path.dentry; + h_parent = au_dget_parent_lock(d, AuLsc_I_PARENT); + if (!wbrtop) + mutex_unlock(&mtx); + /* mnt_want_write() is unnecessary here */ + h_dir = d_inode(h_parent); + inode = file_inode(file); + /* no delegation since it is just created */ + if (inode->i_nlink) + err = vfsub_unlink(h_dir, &file->f_path, /*delegated*/NULL, + /*force*/0); + inode_unlock(h_dir); + dput(h_parent); + if (unlikely(err)) { + if (!silent) + pr_err("unlink %s(%d)\n", fpath, err); + goto out; + } + + err = -EINVAL; + if (unlikely(sb && sb == d->d_sb)) { + if (!silent) + pr_err("%s must be outside\n", fpath); + goto out; + } + if (unlikely(au_test_fs_bad_xino(d->d_sb))) { + if (!silent) + pr_err("xino doesn't support %s(%s)\n", + fpath, au_sbtype(d->d_sb)); + goto out; + } + return file; /* success */ + +out: + fput(file); + file = ERR_PTR(err); + return file; +} + +/* + * create a new xinofile at the same place/path as @base. + */ +struct file *au_xino_create2(struct super_block *sb, struct path *base, + struct file *copy_src) +{ + struct file *file; + struct dentry *dentry; + struct inode *dir, *delegated; + struct qstr *name; + struct path ppath, path; + int err, do_unlock; + struct au_xino_lock_dir ldir; + + do_unlock = 1; + au_xino_lock_dir(sb, base, &ldir); + dentry = base->dentry; + ppath.dentry = dentry->d_parent; /* dir inode is locked */ + ppath.mnt = base->mnt; + dir = d_inode(ppath.dentry); + IMustLock(dir); + + name = &dentry->d_name; + path.dentry = vfsub_lookup_one_len(name->name, &ppath, name->len); + if (IS_ERR(path.dentry)) { + file = (void *)path.dentry; + pr_err("%pd lookup err %ld\n", dentry, PTR_ERR(path.dentry)); + goto out; + } + + /* no need to mnt_want_write() since we call dentry_open() later */ + err = vfs_create(dir, path.dentry, 0666, NULL); + if (unlikely(err)) { + file = ERR_PTR(err); + pr_err("%pd create err %d\n", dentry, err); + goto out_dput; + } + + path.mnt = base->mnt; + file = vfsub_dentry_open(&path, + O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE + /* | __FMODE_NONOTIFY */); + if (IS_ERR(file)) { + pr_err("%pd open err %ld\n", dentry, PTR_ERR(file)); + goto out_dput; + } + + delegated = NULL; + err = vfsub_unlink(dir, &file->f_path, &delegated, /*force*/0); + au_xino_unlock_dir(&ldir); + do_unlock = 0; + if (unlikely(err == -EWOULDBLOCK)) { + pr_warn("cannot retry for NFSv4 delegation" + " for an internal unlink\n"); + iput(delegated); + } + if (unlikely(err)) { + pr_err("%pd unlink err %d\n", dentry, err); + goto out_fput; + } + + if (copy_src) { + /* no one can touch copy_src xino */ + err = au_copy_file(file, copy_src, vfsub_f_size_read(copy_src)); + if (unlikely(err)) { + pr_err("%pd copy err %d\n", dentry, err); + goto out_fput; + } + } + goto out_dput; /* success */ + +out_fput: + fput(file); + file = ERR_PTR(err); +out_dput: + dput(path.dentry); +out: + if (do_unlock) + au_xino_unlock_dir(&ldir); + return file; +} + +struct file *au_xino_file1(struct au_xino *xi) +{ + struct file *file; + unsigned int u, nfile; + + file = NULL; + nfile = xi->xi_nfile; + for (u = 0; u < nfile; u++) { + file = xi->xi_file[u]; + if (file) + break; + } + + return file; +} + +static int au_xino_file_set(struct au_xino *xi, int idx, struct file *file) +{ + int err; + struct file *f; + void *p; + + if (file) + get_file(file); + + err = 0; + f = NULL; + if (idx < xi->xi_nfile) { + f = xi->xi_file[idx]; + if (f) + fput(f); + } else { + p = au_kzrealloc(xi->xi_file, + sizeof(*xi->xi_file) * xi->xi_nfile, + sizeof(*xi->xi_file) * (idx + 1), + GFP_NOFS, /*may_shrink*/0); + if (p) { + MtxMustLock(&xi->xi_mtx); + xi->xi_file = p; + xi->xi_nfile = idx + 1; + } else { + err = -ENOMEM; + if (file) + fput(file); + goto out; + } + } + xi->xi_file[idx] = file; + +out: + return err; +} + +/* + * if @xinew->xi is not set, then create new xigen file. + */ +struct file *au_xi_new(struct super_block *sb, struct au_xi_new *xinew) +{ + struct file *file; + int err; + + SiMustAnyLock(sb); + + file = au_xino_create2(sb, xinew->base, xinew->copy_src); + if (IS_ERR(file)) { + err = PTR_ERR(file); + pr_err("%s[%d], err %d\n", + xinew->xi ? "xino" : "xigen", + xinew->idx, err); + goto out; + } + + if (xinew->xi) + err = au_xino_file_set(xinew->xi, xinew->idx, file); + else { + BUG(); + /* todo: make xigen file an array */ + /* err = au_xigen_file_set(sb, xinew->idx, file); */ + } + fput(file); + if (unlikely(err)) + file = ERR_PTR(err); + +out: + return file; +} + +/* ---------------------------------------------------------------------- */ + +/* + * truncate xino files + */ +static int au_xino_do_trunc(struct super_block *sb, aufs_bindex_t bindex, + int idx, struct kstatfs *st) +{ + int err; + blkcnt_t blocks; + struct file *file, *new_xino; + struct au_xi_new xinew = { + .idx = idx + }; + + err = 0; + xinew.xi = au_sbr(sb, bindex)->br_xino; + file = au_xino_file(xinew.xi, idx); + if (!file) + goto out; + + xinew.base = &file->f_path; + err = vfs_statfs(xinew.base, st); + if (unlikely(err)) { + AuErr1("statfs err %d, ignored\n", err); + err = 0; + goto out; + } + + blocks = file_inode(file)->i_blocks; + pr_info("begin truncating xino(b%d-%d), ib%llu, %llu/%llu free blks\n", + bindex, idx, (u64)blocks, st->f_bfree, st->f_blocks); + + xinew.copy_src = file; + new_xino = au_xi_new(sb, &xinew); + if (IS_ERR(new_xino)) { + err = PTR_ERR(new_xino); + pr_err("xino(b%d-%d), err %d, ignored\n", bindex, idx, err); + goto out; + } + + err = vfs_statfs(&new_xino->f_path, st); + if (!err) + pr_info("end truncating xino(b%d-%d), ib%llu, %llu/%llu free blks\n", + bindex, idx, (u64)file_inode(new_xino)->i_blocks, + st->f_bfree, st->f_blocks); + else { + AuErr1("statfs err %d, ignored\n", err); + err = 0; + } + +out: + return err; +} + +int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex, int idx_begin) +{ + int err, i; + unsigned long jiffy; + aufs_bindex_t bbot; + struct kstatfs *st; + struct au_branch *br; + struct au_xino *xi; + + err = -ENOMEM; + st = kmalloc(sizeof(*st), GFP_NOFS); + if (unlikely(!st)) + goto out; + + err = -EINVAL; + bbot = au_sbbot(sb); + if (unlikely(bindex < 0 || bbot < bindex)) + goto out_st; + + err = 0; + jiffy = jiffies; + br = au_sbr(sb, bindex); + xi = br->br_xino; + for (i = idx_begin; !err && i < xi->xi_nfile; i++) + err = au_xino_do_trunc(sb, bindex, i, st); + if (!err) + au_sbi(sb)->si_xino_jiffy = jiffy; + +out_st: + au_kfree_rcu(st); +out: + return err; +} + +struct xino_do_trunc_args { + struct super_block *sb; + struct au_branch *br; + int idx; +}; + +static void xino_do_trunc(void *_args) +{ + struct xino_do_trunc_args *args = _args; + struct super_block *sb; + struct au_branch *br; + struct inode *dir; + int err, idx; + aufs_bindex_t bindex; + + err = 0; + sb = args->sb; + dir = d_inode(sb->s_root); + br = args->br; + idx = args->idx; + + si_noflush_write_lock(sb); + ii_read_lock_parent(dir); + bindex = au_br_index(sb, br->br_id); + err = au_xino_trunc(sb, bindex, idx); + ii_read_unlock(dir); + if (unlikely(err)) + pr_warn("err b%d, (%d)\n", bindex, err); + atomic_dec(&br->br_xino->xi_truncating); + au_lcnt_dec(&br->br_count); + si_write_unlock(sb); + au_nwt_done(&au_sbi(sb)->si_nowait); + au_kfree_rcu(args); +} + +/* + * returns the index in the xi_file array whose corresponding file is necessary + * to truncate, or -1 which means no need to truncate. + */ +static int xino_trunc_test(struct super_block *sb, struct au_branch *br) +{ + int err; + unsigned int u; + struct kstatfs st; + struct au_sbinfo *sbinfo; + struct au_xino *xi; + struct file *file; + + /* todo: si_xino_expire and the ratio should be customizable */ + sbinfo = au_sbi(sb); + if (time_before(jiffies, + sbinfo->si_xino_jiffy + sbinfo->si_xino_expire)) + return -1; + + /* truncation border */ + xi = br->br_xino; + for (u = 0; u < xi->xi_nfile; u++) { + file = au_xino_file(xi, u); + if (!file) + continue; + + err = vfs_statfs(&file->f_path, &st); + if (unlikely(err)) { + AuErr1("statfs err %d, ignored\n", err); + return -1; + } + if (div64_u64(st.f_bfree * 100, st.f_blocks) + >= AUFS_XINO_DEF_TRUNC) + return u; + } + + return -1; +} + +static void xino_try_trunc(struct super_block *sb, struct au_branch *br) +{ + int idx; + struct xino_do_trunc_args *args; + int wkq_err; + + idx = xino_trunc_test(sb, br); + if (idx < 0) + return; + + if (atomic_inc_return(&br->br_xino->xi_truncating) > 1) + goto out; + + /* lock and kfree() will be called in trunc_xino() */ + args = kmalloc(sizeof(*args), GFP_NOFS); + if (unlikely(!args)) { + AuErr1("no memory\n"); + goto out; + } + + au_lcnt_inc(&br->br_count); + args->sb = sb; + args->br = br; + args->idx = idx; + wkq_err = au_wkq_nowait(xino_do_trunc, args, sb, /*flags*/0); + if (!wkq_err) + return; /* success */ + + pr_err("wkq %d\n", wkq_err); + au_lcnt_dec(&br->br_count); + au_kfree_rcu(args); + +out: + atomic_dec(&br->br_xino->xi_truncating); +} + +/* ---------------------------------------------------------------------- */ + +struct au_xi_calc { + int idx; + loff_t pos; +}; + +static void au_xi_calc(struct super_block *sb, ino_t h_ino, + struct au_xi_calc *calc) +{ + loff_t maxent; + + maxent = au_xi_maxent(sb); + calc->idx = div64_u64_rem(h_ino, maxent, &calc->pos); + calc->pos *= sizeof(ino_t); +} + +static int au_xino_do_new_async(struct super_block *sb, struct au_branch *br, + struct au_xi_calc *calc) +{ + int err; + struct file *file; + struct au_xino *xi = br->br_xino; + struct au_xi_new xinew = { + .xi = xi + }; + + SiMustAnyLock(sb); + + err = 0; + if (!xi) + goto out; + + mutex_lock(&xi->xi_mtx); + file = au_xino_file(xi, calc->idx); + if (file) + goto out_mtx; + + file = au_xino_file(xi, /*idx*/-1); + AuDebugOn(!file); + xinew.idx = calc->idx; + xinew.base = &file->f_path; + /* xinew.copy_src = NULL; */ + file = au_xi_new(sb, &xinew); + if (IS_ERR(file)) + err = PTR_ERR(file); + +out_mtx: + mutex_unlock(&xi->xi_mtx); +out: + return err; +} + +struct au_xino_do_new_async_args { + struct super_block *sb; + struct au_branch *br; + struct au_xi_calc calc; + ino_t ino; +}; + +struct au_xi_writing { + struct hlist_bl_node node; + ino_t h_ino, ino; +}; + +static int au_xino_do_write(struct file *file, struct au_xi_calc *calc, + ino_t ino); + +static void au_xino_call_do_new_async(void *args) +{ + struct au_xino_do_new_async_args *a = args; + struct au_branch *br; + struct super_block *sb; + struct au_sbinfo *sbi; + struct inode *root; + struct file *file; + struct au_xi_writing *del, *p; + struct hlist_bl_head *hbl; + struct hlist_bl_node *pos; + int err; + + br = a->br; + sb = a->sb; + sbi = au_sbi(sb); + si_noflush_read_lock(sb); + root = d_inode(sb->s_root); + ii_read_lock_child(root); + err = au_xino_do_new_async(sb, br, &a->calc); + if (unlikely(err)) { + AuIOErr("err %d\n", err); + goto out; + } + + file = au_xino_file(br->br_xino, a->calc.idx); + AuDebugOn(!file); + err = au_xino_do_write(file, &a->calc, a->ino); + if (unlikely(err)) { + AuIOErr("err %d\n", err); + goto out; + } + + del = NULL; + hbl = &br->br_xino->xi_writing; + hlist_bl_lock(hbl); + au_hbl_for_each(pos, hbl) { + p = container_of(pos, struct au_xi_writing, node); + if (p->ino == a->ino) { + del = p; + hlist_bl_del(&p->node); + break; + } + } + hlist_bl_unlock(hbl); + au_kfree_rcu(del); + +out: + au_lcnt_dec(&br->br_count); + ii_read_unlock(root); + si_read_unlock(sb); + au_nwt_done(&sbi->si_nowait); + au_kfree_rcu(a); +} + +/* + * create a new xino file asynchronously + */ +static int au_xino_new_async(struct super_block *sb, struct au_branch *br, + struct au_xi_calc *calc, ino_t ino) +{ + int err; + struct au_xino_do_new_async_args *arg; + + err = -ENOMEM; + arg = kmalloc(sizeof(*arg), GFP_NOFS); + if (unlikely(!arg)) + goto out; + + arg->sb = sb; + arg->br = br; + arg->calc = *calc; + arg->ino = ino; + au_lcnt_inc(&br->br_count); + err = au_wkq_nowait(au_xino_call_do_new_async, arg, sb, AuWkq_NEST); + if (unlikely(err)) { + pr_err("wkq %d\n", err); + au_lcnt_dec(&br->br_count); + au_kfree_rcu(arg); + } + +out: + return err; +} + +/* + * read @ino from xinofile for the specified branch{@sb, @bindex} + * at the position of @h_ino. + */ +int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, + ino_t *ino) +{ + int err; + ssize_t sz; + struct au_xi_calc calc; + struct au_sbinfo *sbinfo; + struct file *file; + struct au_xino *xi; + struct hlist_bl_head *hbl; + struct hlist_bl_node *pos; + struct au_xi_writing *p; + + *ino = 0; + if (!au_opt_test(au_mntflags(sb), XINO)) + return 0; /* no xino */ + + err = 0; + au_xi_calc(sb, h_ino, &calc); + xi = au_sbr(sb, bindex)->br_xino; + file = au_xino_file(xi, calc.idx); + if (!file) { + hbl = &xi->xi_writing; + hlist_bl_lock(hbl); + au_hbl_for_each(pos, hbl) { + p = container_of(pos, struct au_xi_writing, node); + if (p->h_ino == h_ino) { + AuDbg("hi%llu, i%llu, found\n", + (u64)p->h_ino, (u64)p->ino); + *ino = p->ino; + break; + } + } + hlist_bl_unlock(hbl); + return 0; + } else if (vfsub_f_size_read(file) < calc.pos + sizeof(*ino)) + return 0; /* no xino */ + + sbinfo = au_sbi(sb); + sz = xino_fread(file, ino, sizeof(*ino), &calc.pos); + if (sz == sizeof(*ino)) + return 0; /* success */ + + err = sz; + if (unlikely(sz >= 0)) { + err = -EIO; + AuIOErr("xino read error (%zd)\n", sz); + } + return err; +} + +static int au_xino_do_write(struct file *file, struct au_xi_calc *calc, + ino_t ino) +{ + ssize_t sz; + + sz = xino_fwrite(file, &ino, sizeof(ino), &calc->pos); + if (sz == sizeof(ino)) + return 0; /* success */ + + AuIOErr("write failed (%zd)\n", sz); + return -EIO; +} + +/* + * write @ino to the xinofile for the specified branch{@sb, @bindex} + * at the position of @h_ino. + * even if @ino is zero, it is written to the xinofile and means no entry. + * if the size of the xino file on a specific filesystem exceeds the watermark, + * try truncating it. + */ +int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, + ino_t ino) +{ + int err; + unsigned int mnt_flags; + struct au_xi_calc calc; + struct file *file; + struct au_branch *br; + struct au_xino *xi; + struct au_xi_writing *p; + + SiMustAnyLock(sb); + + mnt_flags = au_mntflags(sb); + if (!au_opt_test(mnt_flags, XINO)) + return 0; + + au_xi_calc(sb, h_ino, &calc); + br = au_sbr(sb, bindex); + xi = br->br_xino; + file = au_xino_file(xi, calc.idx); + if (!file) { + /* store the inum pair into the list */ + p = kmalloc(sizeof(*p), GFP_NOFS | __GFP_NOFAIL); + p->h_ino = h_ino; + p->ino = ino; + au_hbl_add(&p->node, &xi->xi_writing); + + /* create and write a new xino file asynchronously */ + err = au_xino_new_async(sb, br, &calc, ino); + if (!err) + return 0; /* success */ + goto out; + } + + err = au_xino_do_write(file, &calc, ino); + if (!err) { + br = au_sbr(sb, bindex); + if (au_opt_test(mnt_flags, TRUNC_XINO) + && au_test_fs_trunc_xino(au_br_sb(br))) + xino_try_trunc(sb, br); + return 0; /* success */ + } + +out: + AuIOErr("write failed (%d)\n", err); + return -EIO; +} + +static ssize_t xino_fread_wkq(struct file *file, void *buf, size_t size, + loff_t *pos); + +/* todo: unnecessary to support mmap_sem since kernel-space? */ +ssize_t xino_fread(struct file *file, void *kbuf, size_t size, loff_t *pos) +{ + ssize_t err; + int i; + const int prevent_endless = 10; + + i = 0; + do { + err = vfsub_read_k(file, kbuf, size, pos); + if (err == -EINTR + && !au_wkq_test() + && fatal_signal_pending(current)) { + err = xino_fread_wkq(file, kbuf, size, pos); + BUG_ON(err == -EINTR); + } + } while (i++ < prevent_endless + && (err == -EAGAIN || err == -EINTR)); + +#if 0 /* reserved for future use */ + if (err > 0) + fsnotify_access(file->f_path.dentry); +#endif + + return err; +} + +struct xino_fread_args { + ssize_t *errp; + struct file *file; + void *buf; + size_t size; + loff_t *pos; +}; + +static void call_xino_fread(void *args) +{ + struct xino_fread_args *a = args; + *a->errp = xino_fread(a->file, a->buf, a->size, a->pos); +} + +static ssize_t xino_fread_wkq(struct file *file, void *buf, size_t size, + loff_t *pos) +{ + ssize_t err; + int wkq_err; + struct xino_fread_args args = { + .errp = &err, + .file = file, + .buf = buf, + .size = size, + .pos = pos + }; + + wkq_err = au_wkq_wait(call_xino_fread, &args); + if (unlikely(wkq_err)) + err = wkq_err; + + return err; +} + +static ssize_t xino_fwrite_wkq(struct file *file, void *buf, size_t size, + loff_t *pos); + +static ssize_t do_xino_fwrite(struct file *file, void *kbuf, size_t size, + loff_t *pos) +{ + ssize_t err; + int i; + const int prevent_endless = 10; + + i = 0; + do { + err = vfsub_write_k(file, kbuf, size, pos); + if (err == -EINTR + && !au_wkq_test() + && fatal_signal_pending(current)) { + err = xino_fwrite_wkq(file, kbuf, size, pos); + BUG_ON(err == -EINTR); + } + } while (i++ < prevent_endless + && (err == -EAGAIN || err == -EINTR)); + +#if 0 /* reserved for future use */ + if (err > 0) + fsnotify_modify(file->f_path.dentry); +#endif + + return err; +} + +struct do_xino_fwrite_args { + ssize_t *errp; + struct file *file; + void *buf; + size_t size; + loff_t *pos; +}; + +static void call_do_xino_fwrite(void *args) +{ + struct do_xino_fwrite_args *a = args; + *a->errp = do_xino_fwrite(a->file, a->buf, a->size, a->pos); +} + +static ssize_t xino_fwrite_wkq(struct file *file, void *buf, size_t size, + loff_t *pos) +{ + ssize_t err; + int wkq_err; + struct do_xino_fwrite_args args = { + .errp = &err, + .file = file, + .buf = buf, + .size = size, + .pos = pos + }; + + /* + * it breaks RLIMIT_FSIZE and normal user's limit, + * users should care about quota and real 'filesystem full.' + */ + wkq_err = au_wkq_wait(call_do_xino_fwrite, &args); + if (unlikely(wkq_err)) + err = wkq_err; + + return err; +} + +ssize_t xino_fwrite(struct file *file, void *buf, size_t size, loff_t *pos) +{ + ssize_t err; + + if (rlimit(RLIMIT_FSIZE) == RLIM_INFINITY) { + lockdep_off(); + err = do_xino_fwrite(file, buf, size, pos); + lockdep_on(); + } else { + lockdep_off(); + err = xino_fwrite_wkq(file, buf, size, pos); + lockdep_on(); + } + + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* + * inode number bitmap + */ +static const int page_bits = (int)PAGE_SIZE * BITS_PER_BYTE; +static ino_t xib_calc_ino(unsigned long pindex, int bit) +{ + ino_t ino; + + AuDebugOn(bit < 0 || page_bits <= bit); + ino = AUFS_FIRST_INO + pindex * page_bits + bit; + return ino; +} + +static void xib_calc_bit(ino_t ino, unsigned long *pindex, int *bit) +{ + AuDebugOn(ino < AUFS_FIRST_INO); + ino -= AUFS_FIRST_INO; + *pindex = ino / page_bits; + *bit = ino % page_bits; +} + +static int xib_pindex(struct super_block *sb, unsigned long pindex) +{ + int err; + loff_t pos; + ssize_t sz; + struct au_sbinfo *sbinfo; + struct file *xib; + unsigned long *p; + + sbinfo = au_sbi(sb); + MtxMustLock(&sbinfo->si_xib_mtx); + AuDebugOn(pindex > ULONG_MAX / PAGE_SIZE + || !au_opt_test(sbinfo->si_mntflags, XINO)); + + if (pindex == sbinfo->si_xib_last_pindex) + return 0; + + xib = sbinfo->si_xib; + p = sbinfo->si_xib_buf; + pos = sbinfo->si_xib_last_pindex; + pos *= PAGE_SIZE; + sz = xino_fwrite(xib, p, PAGE_SIZE, &pos); + if (unlikely(sz != PAGE_SIZE)) + goto out; + + pos = pindex; + pos *= PAGE_SIZE; + if (vfsub_f_size_read(xib) >= pos + PAGE_SIZE) + sz = xino_fread(xib, p, PAGE_SIZE, &pos); + else { + memset(p, 0, PAGE_SIZE); + sz = xino_fwrite(xib, p, PAGE_SIZE, &pos); + } + if (sz == PAGE_SIZE) { + sbinfo->si_xib_last_pindex = pindex; + return 0; /* success */ + } + +out: + AuIOErr1("write failed (%zd)\n", sz); + err = sz; + if (sz >= 0) + err = -EIO; + return err; +} + +static void au_xib_clear_bit(struct inode *inode) +{ + int err, bit; + unsigned long pindex; + struct super_block *sb; + struct au_sbinfo *sbinfo; + + AuDebugOn(inode->i_nlink); + + sb = inode->i_sb; + xib_calc_bit(inode->i_ino, &pindex, &bit); + AuDebugOn(page_bits <= bit); + sbinfo = au_sbi(sb); + mutex_lock(&sbinfo->si_xib_mtx); + err = xib_pindex(sb, pindex); + if (!err) { + clear_bit(bit, sbinfo->si_xib_buf); + sbinfo->si_xib_next_bit = bit; + } + mutex_unlock(&sbinfo->si_xib_mtx); +} + +/* ---------------------------------------------------------------------- */ + +/* + * truncate a xino bitmap file + */ + +/* todo: slow */ +static int do_xib_restore(struct super_block *sb, struct file *file, void *page) +{ + int err, bit; + ssize_t sz; + unsigned long pindex; + loff_t pos, pend; + struct au_sbinfo *sbinfo; + ino_t *ino; + unsigned long *p; + + err = 0; + sbinfo = au_sbi(sb); + MtxMustLock(&sbinfo->si_xib_mtx); + p = sbinfo->si_xib_buf; + pend = vfsub_f_size_read(file); + pos = 0; + while (pos < pend) { + sz = xino_fread(file, page, PAGE_SIZE, &pos); + err = sz; + if (unlikely(sz <= 0)) + goto out; + + err = 0; + for (ino = page; sz > 0; ino++, sz -= sizeof(ino)) { + if (unlikely(*ino < AUFS_FIRST_INO)) + continue; + + xib_calc_bit(*ino, &pindex, &bit); + AuDebugOn(page_bits <= bit); + err = xib_pindex(sb, pindex); + if (!err) + set_bit(bit, p); + else + goto out; + } + } + +out: + return err; +} + +static int xib_restore(struct super_block *sb) +{ + int err, i; + unsigned int nfile; + aufs_bindex_t bindex, bbot; + void *page; + struct au_branch *br; + struct au_xino *xi; + struct file *file; + + err = -ENOMEM; + page = (void *)__get_free_page(GFP_NOFS); + if (unlikely(!page)) + goto out; + + err = 0; + bbot = au_sbbot(sb); + for (bindex = 0; !err && bindex <= bbot; bindex++) + if (!bindex || is_sb_shared(sb, bindex, bindex - 1) < 0) { + br = au_sbr(sb, bindex); + xi = br->br_xino; + nfile = xi->xi_nfile; + for (i = 0; i < nfile; i++) { + file = au_xino_file(xi, i); + if (file) + err = do_xib_restore(sb, file, page); + } + } else + AuDbg("skip shared b%d\n", bindex); + free_page((unsigned long)page); + +out: + return err; +} + +int au_xib_trunc(struct super_block *sb) +{ + int err; + ssize_t sz; + loff_t pos; + struct au_sbinfo *sbinfo; + unsigned long *p; + struct file *file; + + SiMustWriteLock(sb); + + err = 0; + sbinfo = au_sbi(sb); + if (!au_opt_test(sbinfo->si_mntflags, XINO)) + goto out; + + file = sbinfo->si_xib; + if (vfsub_f_size_read(file) <= PAGE_SIZE) + goto out; + + file = au_xino_create2(sb, &sbinfo->si_xib->f_path, NULL); + err = PTR_ERR(file); + if (IS_ERR(file)) + goto out; + fput(sbinfo->si_xib); + sbinfo->si_xib = file; + + p = sbinfo->si_xib_buf; + memset(p, 0, PAGE_SIZE); + pos = 0; + sz = xino_fwrite(sbinfo->si_xib, p, PAGE_SIZE, &pos); + if (unlikely(sz != PAGE_SIZE)) { + err = sz; + AuIOErr("err %d\n", err); + if (sz >= 0) + err = -EIO; + goto out; + } + + mutex_lock(&sbinfo->si_xib_mtx); + /* mnt_want_write() is unnecessary here */ + err = xib_restore(sb); + mutex_unlock(&sbinfo->si_xib_mtx); + +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +struct au_xino *au_xino_alloc(unsigned int nfile) +{ + struct au_xino *xi; + + xi = kzalloc(sizeof(*xi), GFP_NOFS); + if (unlikely(!xi)) + goto out; + xi->xi_nfile = nfile; + xi->xi_file = kcalloc(nfile, sizeof(*xi->xi_file), GFP_NOFS); + if (unlikely(!xi->xi_file)) + goto out_free; + + xi->xi_nondir.total = 8; /* initial size */ + xi->xi_nondir.array = kcalloc(xi->xi_nondir.total, sizeof(ino_t), + GFP_NOFS); + if (unlikely(!xi->xi_nondir.array)) + goto out_file; + + spin_lock_init(&xi->xi_nondir.spin); + init_waitqueue_head(&xi->xi_nondir.wqh); + mutex_init(&xi->xi_mtx); + INIT_HLIST_BL_HEAD(&xi->xi_writing); + atomic_set(&xi->xi_truncating, 0); + kref_init(&xi->xi_kref); + goto out; /* success */ + +out_file: + au_kfree_try_rcu(xi->xi_file); +out_free: + au_kfree_rcu(xi); + xi = NULL; +out: + return xi; +} + +static int au_xino_init(struct au_branch *br, int idx, struct file *file) +{ + int err; + struct au_xino *xi; + + err = 0; + xi = au_xino_alloc(idx + 1); + if (unlikely(!xi)) { + err = -ENOMEM; + goto out; + } + + if (file) + get_file(file); + xi->xi_file[idx] = file; + AuDebugOn(br->br_xino); + br->br_xino = xi; + +out: + return err; +} + +static void au_xino_release(struct kref *kref) +{ + struct au_xino *xi; + int i; + unsigned long ul; + struct hlist_bl_head *hbl; + struct hlist_bl_node *pos, *n; + struct au_xi_writing *p; + + xi = container_of(kref, struct au_xino, xi_kref); + for (i = 0; i < xi->xi_nfile; i++) + if (xi->xi_file[i]) + fput(xi->xi_file[i]); + for (i = xi->xi_nondir.total - 1; i >= 0; i--) + AuDebugOn(xi->xi_nondir.array[i]); + mutex_destroy(&xi->xi_mtx); + hbl = &xi->xi_writing; + ul = au_hbl_count(hbl); + if (unlikely(ul)) { + pr_warn("xi_writing %lu\n", ul); + hlist_bl_lock(hbl); + hlist_bl_for_each_entry_safe(p, pos, n, hbl, node) { + hlist_bl_del(&p->node); + /* kmemleak reported au_kfree_rcu() doesn't free it */ + kfree(p); + } + hlist_bl_unlock(hbl); + } + au_kfree_try_rcu(xi->xi_file); + au_kfree_try_rcu(xi->xi_nondir.array); + au_kfree_rcu(xi); +} + +int au_xino_put(struct au_branch *br) +{ + int ret; + struct au_xino *xi; + + ret = 0; + xi = br->br_xino; + if (xi) { + br->br_xino = NULL; + ret = kref_put(&xi->xi_kref, au_xino_release); + } + + return ret; +} + +/* ---------------------------------------------------------------------- */ + +/* + * xino mount option handlers + */ + +/* xino bitmap */ +static void xino_clear_xib(struct super_block *sb) +{ + struct au_sbinfo *sbinfo; + + SiMustWriteLock(sb); + + sbinfo = au_sbi(sb); + if (sbinfo->si_xib) + fput(sbinfo->si_xib); + sbinfo->si_xib = NULL; + if (sbinfo->si_xib_buf) + free_page((unsigned long)sbinfo->si_xib_buf); + sbinfo->si_xib_buf = NULL; +} + +static int au_xino_set_xib(struct super_block *sb, struct path *path) +{ + int err; + loff_t pos; + struct au_sbinfo *sbinfo; + struct file *file; + struct super_block *xi_sb; + + SiMustWriteLock(sb); + + sbinfo = au_sbi(sb); + file = au_xino_create2(sb, path, sbinfo->si_xib); + err = PTR_ERR(file); + if (IS_ERR(file)) + goto out; + if (sbinfo->si_xib) + fput(sbinfo->si_xib); + sbinfo->si_xib = file; + xi_sb = file_inode(file)->i_sb; + sbinfo->si_ximaxent = xi_sb->s_maxbytes; + if (unlikely(sbinfo->si_ximaxent < PAGE_SIZE)) { + err = -EIO; + pr_err("s_maxbytes(%llu) on %s is too small\n", + (u64)sbinfo->si_ximaxent, au_sbtype(xi_sb)); + goto out_unset; + } + sbinfo->si_ximaxent /= sizeof(ino_t); + + err = -ENOMEM; + if (!sbinfo->si_xib_buf) + sbinfo->si_xib_buf = (void *)get_zeroed_page(GFP_NOFS); + if (unlikely(!sbinfo->si_xib_buf)) + goto out_unset; + + sbinfo->si_xib_last_pindex = 0; + sbinfo->si_xib_next_bit = 0; + if (vfsub_f_size_read(file) < PAGE_SIZE) { + pos = 0; + err = xino_fwrite(file, sbinfo->si_xib_buf, PAGE_SIZE, &pos); + if (unlikely(err != PAGE_SIZE)) + goto out_free; + } + err = 0; + goto out; /* success */ + +out_free: + if (sbinfo->si_xib_buf) + free_page((unsigned long)sbinfo->si_xib_buf); + sbinfo->si_xib_buf = NULL; + if (err >= 0) + err = -EIO; +out_unset: + fput(sbinfo->si_xib); + sbinfo->si_xib = NULL; +out: + AuTraceErr(err); + return err; +} + +/* xino for each branch */ +static void xino_clear_br(struct super_block *sb) +{ + aufs_bindex_t bindex, bbot; + struct au_branch *br; + + bbot = au_sbbot(sb); + for (bindex = 0; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + AuDebugOn(!br); + au_xino_put(br); + } +} + +static void au_xino_set_br_shared(struct super_block *sb, struct au_branch *br, + aufs_bindex_t bshared) +{ + struct au_branch *brshared; + + brshared = au_sbr(sb, bshared); + AuDebugOn(!brshared->br_xino); + AuDebugOn(!brshared->br_xino->xi_file); + if (br->br_xino != brshared->br_xino) { + au_xino_get(brshared); + au_xino_put(br); + br->br_xino = brshared->br_xino; + } +} + +struct au_xino_do_set_br { + struct au_branch *br; + ino_t h_ino; + aufs_bindex_t bshared; +}; + +static int au_xino_do_set_br(struct super_block *sb, struct path *path, + struct au_xino_do_set_br *args) +{ + int err; + struct au_xi_calc calc; + struct file *file; + struct au_branch *br; + struct au_xi_new xinew = { + .base = path + }; + + br = args->br; + xinew.xi = br->br_xino; + au_xi_calc(sb, args->h_ino, &calc); + xinew.copy_src = au_xino_file(xinew.xi, calc.idx); + if (args->bshared >= 0) + /* shared xino */ + au_xino_set_br_shared(sb, br, args->bshared); + else if (!xinew.xi) { + /* new xino */ + err = au_xino_init(br, calc.idx, xinew.copy_src); + if (unlikely(err)) + goto out; + } + + /* force re-creating */ + xinew.xi = br->br_xino; + xinew.idx = calc.idx; + mutex_lock(&xinew.xi->xi_mtx); + file = au_xi_new(sb, &xinew); + mutex_unlock(&xinew.xi->xi_mtx); + err = PTR_ERR(file); + if (IS_ERR(file)) + goto out; + AuDebugOn(!file); + + err = au_xino_do_write(file, &calc, AUFS_ROOT_INO); + if (unlikely(err)) + au_xino_put(br); + +out: + AuTraceErr(err); + return err; +} + +static int au_xino_set_br(struct super_block *sb, struct path *path) +{ + int err; + aufs_bindex_t bindex, bbot; + struct au_xino_do_set_br args; + struct inode *inode; + + SiMustWriteLock(sb); + + bbot = au_sbbot(sb); + inode = d_inode(sb->s_root); + for (bindex = 0; bindex <= bbot; bindex++) { + args.h_ino = au_h_iptr(inode, bindex)->i_ino; + args.br = au_sbr(sb, bindex); + args.bshared = is_sb_shared(sb, bindex, bindex - 1); + err = au_xino_do_set_br(sb, path, &args); + if (unlikely(err)) + break; + } + + AuTraceErr(err); + return err; +} + +void au_xino_clr(struct super_block *sb) +{ + struct au_sbinfo *sbinfo; + + au_xigen_clr(sb); + xino_clear_xib(sb); + xino_clear_br(sb); + dbgaufs_brs_del(sb, 0); + sbinfo = au_sbi(sb); + /* lvalue, do not call au_mntflags() */ + au_opt_clr(sbinfo->si_mntflags, XINO); +} + +int au_xino_set(struct super_block *sb, struct au_opt_xino *xiopt, int remount) +{ + int err, skip; + struct dentry *dentry, *parent, *cur_dentry, *cur_parent; + struct qstr *dname, *cur_name; + struct file *cur_xino; + struct au_sbinfo *sbinfo; + struct path *path, *cur_path; + + SiMustWriteLock(sb); + + err = 0; + sbinfo = au_sbi(sb); + path = &xiopt->file->f_path; + dentry = path->dentry; + parent = dget_parent(dentry); + if (remount) { + skip = 0; + cur_xino = sbinfo->si_xib; + if (cur_xino) { + cur_path = &cur_xino->f_path; + cur_dentry = cur_path->dentry; + cur_parent = dget_parent(cur_dentry); + cur_name = &cur_dentry->d_name; + dname = &dentry->d_name; + skip = (cur_parent == parent + && au_qstreq(dname, cur_name)); + dput(cur_parent); + } + if (skip) + goto out; + } + + au_opt_set(sbinfo->si_mntflags, XINO); + err = au_xino_set_xib(sb, path); + /* si_x{read,write} are set */ + if (!err) + err = au_xigen_set(sb, path); + if (!err) + err = au_xino_set_br(sb, path); + if (!err) { + dbgaufs_brs_add(sb, 0, /*topdown*/1); + goto out; /* success */ + } + + /* reset all */ + AuIOErr("failed setting xino(%d).\n", err); + au_xino_clr(sb); + +out: + dput(parent); + return err; +} + +/* + * create a xinofile at the default place/path. + */ +struct file *au_xino_def(struct super_block *sb) +{ + struct file *file; + char *page, *p; + struct au_branch *br; + struct super_block *h_sb; + struct path path; + aufs_bindex_t bbot, bindex, bwr; + + br = NULL; + bbot = au_sbbot(sb); + bwr = -1; + for (bindex = 0; bindex <= bbot; bindex++) { + br = au_sbr(sb, bindex); + if (au_br_writable(br->br_perm) + && !au_test_fs_bad_xino(au_br_sb(br))) { + bwr = bindex; + break; + } + } + + if (bwr >= 0) { + file = ERR_PTR(-ENOMEM); + page = (void *)__get_free_page(GFP_NOFS); + if (unlikely(!page)) + goto out; + path.mnt = au_br_mnt(br); + path.dentry = au_h_dptr(sb->s_root, bwr); + p = d_path(&path, page, PATH_MAX - sizeof(AUFS_XINO_FNAME)); + file = (void *)p; + if (!IS_ERR(p)) { + strcat(p, "/" AUFS_XINO_FNAME); + AuDbg("%s\n", p); + file = au_xino_create(sb, p, /*silent*/0, /*wbrtop*/1); + } + free_page((unsigned long)page); + } else { + file = au_xino_create(sb, AUFS_XINO_DEFPATH, /*silent*/0, + /*wbrtop*/0); + if (IS_ERR(file)) + goto out; + h_sb = file->f_path.dentry->d_sb; + if (unlikely(au_test_fs_bad_xino(h_sb))) { + pr_err("xino doesn't support %s(%s)\n", + AUFS_XINO_DEFPATH, au_sbtype(h_sb)); + fput(file); + file = ERR_PTR(-EINVAL); + } + } + +out: + return file; +} + +/* ---------------------------------------------------------------------- */ + +/* + * initialize the xinofile for the specified branch @br + * at the place/path where @base_file indicates. + * test whether another branch is on the same filesystem or not, + * if found then share the xinofile with another branch. + */ +int au_xino_init_br(struct super_block *sb, struct au_branch *br, ino_t h_ino, + struct path *base) +{ + int err; + struct au_xino_do_set_br args = { + .h_ino = h_ino, + .br = br + }; + + args.bshared = sbr_find_shared(sb, /*btop*/0, au_sbbot(sb), + au_br_sb(br)); + err = au_xino_do_set_br(sb, base, &args); + if (unlikely(err)) + au_xino_put(br); + + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* + * get an unused inode number from bitmap + */ +ino_t au_xino_new_ino(struct super_block *sb) +{ + ino_t ino; + unsigned long *p, pindex, ul, pend; + struct au_sbinfo *sbinfo; + struct file *file; + int free_bit, err; + + if (!au_opt_test(au_mntflags(sb), XINO)) + return iunique(sb, AUFS_FIRST_INO); + + sbinfo = au_sbi(sb); + mutex_lock(&sbinfo->si_xib_mtx); + p = sbinfo->si_xib_buf; + free_bit = sbinfo->si_xib_next_bit; + if (free_bit < page_bits && !test_bit(free_bit, p)) + goto out; /* success */ + free_bit = find_first_zero_bit(p, page_bits); + if (free_bit < page_bits) + goto out; /* success */ + + pindex = sbinfo->si_xib_last_pindex; + for (ul = pindex - 1; ul < ULONG_MAX; ul--) { + err = xib_pindex(sb, ul); + if (unlikely(err)) + goto out_err; + free_bit = find_first_zero_bit(p, page_bits); + if (free_bit < page_bits) + goto out; /* success */ + } + + file = sbinfo->si_xib; + pend = vfsub_f_size_read(file) / PAGE_SIZE; + for (ul = pindex + 1; ul <= pend; ul++) { + err = xib_pindex(sb, ul); + if (unlikely(err)) + goto out_err; + free_bit = find_first_zero_bit(p, page_bits); + if (free_bit < page_bits) + goto out; /* success */ + } + BUG(); + +out: + set_bit(free_bit, p); + sbinfo->si_xib_next_bit = free_bit + 1; + pindex = sbinfo->si_xib_last_pindex; + mutex_unlock(&sbinfo->si_xib_mtx); + ino = xib_calc_ino(pindex, free_bit); + AuDbg("i%lu\n", (unsigned long)ino); + return ino; +out_err: + mutex_unlock(&sbinfo->si_xib_mtx); + AuDbg("i0\n"); + return 0; +} + +/* for s_op->delete_inode() */ +void au_xino_delete_inode(struct inode *inode, const int unlinked) +{ + int err; + unsigned int mnt_flags; + aufs_bindex_t bindex, bbot, bi; + unsigned char try_trunc; + struct au_iinfo *iinfo; + struct super_block *sb; + struct au_hinode *hi; + struct inode *h_inode; + struct au_branch *br; + struct au_xi_calc calc; + struct file *file; + + AuDebugOn(au_is_bad_inode(inode)); + + sb = inode->i_sb; + mnt_flags = au_mntflags(sb); + if (!au_opt_test(mnt_flags, XINO) + || inode->i_ino == AUFS_ROOT_INO) + return; + + if (unlinked) { + au_xigen_inc(inode); + au_xib_clear_bit(inode); + } + + iinfo = au_ii(inode); + bindex = iinfo->ii_btop; + if (bindex < 0) + return; + + try_trunc = !!au_opt_test(mnt_flags, TRUNC_XINO); + hi = au_hinode(iinfo, bindex); + bbot = iinfo->ii_bbot; + for (; bindex <= bbot; bindex++, hi++) { + h_inode = hi->hi_inode; + if (!h_inode + || (!unlinked && h_inode->i_nlink)) + continue; + + /* inode may not be revalidated */ + bi = au_br_index(sb, hi->hi_id); + if (bi < 0) + continue; + + br = au_sbr(sb, bi); + au_xi_calc(sb, h_inode->i_ino, &calc); + file = au_xino_file(br->br_xino, calc.idx); + if (IS_ERR_OR_NULL(file)) + continue; + + err = au_xino_do_write(file, &calc, /*ino*/0); + if (!err && try_trunc + && au_test_fs_trunc_xino(au_br_sb(br))) + xino_try_trunc(sb, br); + } +} + +/* ---------------------------------------------------------------------- */ + +static int au_xinondir_find(struct au_xino *xi, ino_t h_ino) +{ + int found, total, i; + + found = -1; + total = xi->xi_nondir.total; + for (i = 0; i < total; i++) { + if (xi->xi_nondir.array[i] != h_ino) + continue; + found = i; + break; + } + + return found; +} + +static int au_xinondir_expand(struct au_xino *xi) +{ + int err, sz; + ino_t *p; + + BUILD_BUG_ON(KMALLOC_MAX_SIZE > INT_MAX); + + err = -ENOMEM; + sz = xi->xi_nondir.total * sizeof(ino_t); + if (unlikely(sz > KMALLOC_MAX_SIZE / 2)) + goto out; + p = au_kzrealloc(xi->xi_nondir.array, sz, sz << 1, GFP_ATOMIC, + /*may_shrink*/0); + if (p) { + xi->xi_nondir.array = p; + xi->xi_nondir.total <<= 1; + AuDbg("xi_nondir.total %d\n", xi->xi_nondir.total); + err = 0; + } + +out: + return err; +} + +void au_xinondir_leave(struct super_block *sb, aufs_bindex_t bindex, + ino_t h_ino, int idx) +{ + struct au_xino *xi; + + AuDebugOn(!au_opt_test(au_mntflags(sb), XINO)); + xi = au_sbr(sb, bindex)->br_xino; + AuDebugOn(idx < 0 || xi->xi_nondir.total <= idx); + + spin_lock(&xi->xi_nondir.spin); + AuDebugOn(xi->xi_nondir.array[idx] != h_ino); + xi->xi_nondir.array[idx] = 0; + spin_unlock(&xi->xi_nondir.spin); + wake_up_all(&xi->xi_nondir.wqh); +} + +int au_xinondir_enter(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, + int *idx) +{ + int err, found, empty; + struct au_xino *xi; + + err = 0; + *idx = -1; + if (!au_opt_test(au_mntflags(sb), XINO)) + goto out; /* no xino */ + + xi = au_sbr(sb, bindex)->br_xino; + +again: + spin_lock(&xi->xi_nondir.spin); + found = au_xinondir_find(xi, h_ino); + if (found == -1) { + empty = au_xinondir_find(xi, /*h_ino*/0); + if (empty == -1) { + empty = xi->xi_nondir.total; + err = au_xinondir_expand(xi); + if (unlikely(err)) + goto out_unlock; + } + xi->xi_nondir.array[empty] = h_ino; + *idx = empty; + } else { + spin_unlock(&xi->xi_nondir.spin); + wait_event(xi->xi_nondir.wqh, + xi->xi_nondir.array[found] != h_ino); + goto again; + } + +out_unlock: + spin_unlock(&xi->xi_nondir.spin); +out: + return err; +} + +/* ---------------------------------------------------------------------- */ + +int au_xino_path(struct seq_file *seq, struct file *file) +{ + int err; + + err = au_seq_path(seq, &file->f_path); + if (unlikely(err)) + goto out; + +#define Deleted "\\040(deleted)" + seq->count -= sizeof(Deleted) - 1; + AuDebugOn(memcmp(seq->buf + seq->count, Deleted, + sizeof(Deleted) - 1)); +#undef Deleted + +out: + return err; +} diff --git a/include/uapi/linux/aufs_type.h b/include/uapi/linux/aufs_type.h new file mode 100644 index 0000000000000..e91e657a18880 --- /dev/null +++ b/include/uapi/linux/aufs_type.h @@ -0,0 +1,452 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (C) 2005-2021 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __AUFS_TYPE_H__ +#define __AUFS_TYPE_H__ + +#define AUFS_NAME "aufs" + +#ifdef __KERNEL__ +/* + * define it before including all other headers. + * sched.h may use pr_* macros before defining "current", so define the + * no-current version first, and re-define later. + */ +#define pr_fmt(fmt) AUFS_NAME " %s:%d: " fmt, __func__, __LINE__ +#include +#undef pr_fmt +#define pr_fmt(fmt) \ + AUFS_NAME " %s:%d:%.*s[%d]: " fmt, __func__, __LINE__, \ + (int)sizeof(current->comm), current->comm, current->pid +#include +#else +#include +#include +#include +#endif /* __KERNEL__ */ + +#define AUFS_VERSION "5.10.140-20221219" + +/* todo? move this to linux-2.6.19/include/magic.h */ +#define AUFS_SUPER_MAGIC ('a' << 24 | 'u' << 16 | 'f' << 8 | 's') + +/* ---------------------------------------------------------------------- */ + +#ifdef __KERNEL__ +#ifdef CONFIG_AUFS_BRANCH_MAX_127 +typedef int8_t aufs_bindex_t; +#define AUFS_BRANCH_MAX 127 +#else +typedef int16_t aufs_bindex_t; +#ifdef CONFIG_AUFS_BRANCH_MAX_511 +#define AUFS_BRANCH_MAX 511 +#elif defined(CONFIG_AUFS_BRANCH_MAX_1023) +#define AUFS_BRANCH_MAX 1023 +#elif defined(CONFIG_AUFS_BRANCH_MAX_32767) +#define AUFS_BRANCH_MAX 32767 +#endif +#endif + +#ifndef AUFS_BRANCH_MAX +#error unknown CONFIG_AUFS_BRANCH_MAX value +#endif +#endif /* __KERNEL__ */ + +/* ---------------------------------------------------------------------- */ + +#define AUFS_FSTYPE AUFS_NAME + +#define AUFS_ROOT_INO 2 +#define AUFS_FIRST_INO 11 + +#define AUFS_WH_PFX ".wh." +#define AUFS_WH_PFX_LEN ((int)sizeof(AUFS_WH_PFX) - 1) +#define AUFS_WH_TMP_LEN 4 +/* a limit for rmdir/rename a dir and copyup */ +#define AUFS_MAX_NAMELEN (NAME_MAX \ + - AUFS_WH_PFX_LEN * 2 /* doubly whiteouted */\ + - 1 /* dot */\ + - AUFS_WH_TMP_LEN) /* hex */ +#define AUFS_XINO_FNAME "." AUFS_NAME ".xino" +#define AUFS_XINO_DEFPATH "/tmp/" AUFS_XINO_FNAME +#define AUFS_XINO_DEF_SEC 30 /* seconds */ +#define AUFS_XINO_DEF_TRUNC 45 /* percentage */ +#define AUFS_DIRWH_DEF 3 +#define AUFS_RDCACHE_DEF 10 /* seconds */ +#define AUFS_RDCACHE_MAX 3600 /* seconds */ +#define AUFS_RDBLK_DEF 512 /* bytes */ +#define AUFS_RDHASH_DEF 32 +#define AUFS_WKQ_NAME AUFS_NAME "d" +#define AUFS_MFS_DEF_SEC 30 /* seconds */ +#define AUFS_MFS_MAX_SEC 3600 /* seconds */ +#define AUFS_FHSM_CACHE_DEF_SEC 30 /* seconds */ +#define AUFS_PLINK_WARN 50 /* number of plinks in a single bucket */ + +/* pseudo-link maintenace under /proc */ +#define AUFS_PLINK_MAINT_NAME "plink_maint" +#define AUFS_PLINK_MAINT_DIR "fs/" AUFS_NAME +#define AUFS_PLINK_MAINT_PATH AUFS_PLINK_MAINT_DIR "/" AUFS_PLINK_MAINT_NAME + +/* dirren, renamed dir */ +#define AUFS_DR_INFO_PFX AUFS_WH_PFX ".dr." +#define AUFS_DR_BRHINO_NAME AUFS_WH_PFX "hino" +/* whiteouted doubly */ +#define AUFS_WH_DR_INFO_PFX AUFS_WH_PFX AUFS_DR_INFO_PFX +#define AUFS_WH_DR_BRHINO AUFS_WH_PFX AUFS_DR_BRHINO_NAME + +#define AUFS_DIROPQ_NAME AUFS_WH_PFX ".opq" /* whiteouted doubly */ +#define AUFS_WH_DIROPQ AUFS_WH_PFX AUFS_DIROPQ_NAME + +#define AUFS_BASE_NAME AUFS_WH_PFX AUFS_NAME +#define AUFS_PLINKDIR_NAME AUFS_WH_PFX "plnk" +#define AUFS_ORPHDIR_NAME AUFS_WH_PFX "orph" + +/* doubly whiteouted */ +#define AUFS_WH_BASE AUFS_WH_PFX AUFS_BASE_NAME +#define AUFS_WH_PLINKDIR AUFS_WH_PFX AUFS_PLINKDIR_NAME +#define AUFS_WH_ORPHDIR AUFS_WH_PFX AUFS_ORPHDIR_NAME + +/* branch permissions and attributes */ +#define AUFS_BRPERM_RW "rw" +#define AUFS_BRPERM_RO "ro" +#define AUFS_BRPERM_RR "rr" +#define AUFS_BRATTR_COO_REG "coo_reg" +#define AUFS_BRATTR_COO_ALL "coo_all" +#define AUFS_BRATTR_FHSM "fhsm" +#define AUFS_BRATTR_UNPIN "unpin" +#define AUFS_BRATTR_ICEX "icex" +#define AUFS_BRATTR_ICEX_SEC "icexsec" +#define AUFS_BRATTR_ICEX_SYS "icexsys" +#define AUFS_BRATTR_ICEX_TR "icextr" +#define AUFS_BRATTR_ICEX_USR "icexusr" +#define AUFS_BRATTR_ICEX_OTH "icexoth" +#define AUFS_BRRATTR_WH "wh" +#define AUFS_BRWATTR_NLWH "nolwh" +#define AUFS_BRWATTR_MOO "moo" + +#define AuBrPerm_RW 1 /* writable, hardlinkable wh */ +#define AuBrPerm_RO (1 << 1) /* readonly */ +#define AuBrPerm_RR (1 << 2) /* natively readonly */ +#define AuBrPerm_Mask (AuBrPerm_RW | AuBrPerm_RO | AuBrPerm_RR) + +#define AuBrAttr_COO_REG (1 << 3) /* copy-up on open */ +#define AuBrAttr_COO_ALL (1 << 4) +#define AuBrAttr_COO_Mask (AuBrAttr_COO_REG | AuBrAttr_COO_ALL) + +#define AuBrAttr_FHSM (1 << 5) /* file-based hsm */ +#define AuBrAttr_UNPIN (1 << 6) /* rename-able top dir of + branch. meaningless since + linux-3.18-rc1 */ + +/* ignore error in copying XATTR */ +#define AuBrAttr_ICEX_SEC (1 << 7) +#define AuBrAttr_ICEX_SYS (1 << 8) +#define AuBrAttr_ICEX_TR (1 << 9) +#define AuBrAttr_ICEX_USR (1 << 10) +#define AuBrAttr_ICEX_OTH (1 << 11) +#define AuBrAttr_ICEX (AuBrAttr_ICEX_SEC \ + | AuBrAttr_ICEX_SYS \ + | AuBrAttr_ICEX_TR \ + | AuBrAttr_ICEX_USR \ + | AuBrAttr_ICEX_OTH) + +#define AuBrRAttr_WH (1 << 12) /* whiteout-able */ +#define AuBrRAttr_Mask AuBrRAttr_WH + +#define AuBrWAttr_NoLinkWH (1 << 13) /* un-hardlinkable whiteouts */ +#define AuBrWAttr_MOO (1 << 14) /* move-up on open */ +#define AuBrWAttr_Mask (AuBrWAttr_NoLinkWH | AuBrWAttr_MOO) + +#define AuBrAttr_CMOO_Mask (AuBrAttr_COO_Mask | AuBrWAttr_MOO) + +/* #warning test userspace */ +#ifdef __KERNEL__ +#ifndef CONFIG_AUFS_FHSM +#undef AuBrAttr_FHSM +#define AuBrAttr_FHSM 0 +#endif +#ifndef CONFIG_AUFS_XATTR +#undef AuBrAttr_ICEX +#define AuBrAttr_ICEX 0 +#undef AuBrAttr_ICEX_SEC +#define AuBrAttr_ICEX_SEC 0 +#undef AuBrAttr_ICEX_SYS +#define AuBrAttr_ICEX_SYS 0 +#undef AuBrAttr_ICEX_TR +#define AuBrAttr_ICEX_TR 0 +#undef AuBrAttr_ICEX_USR +#define AuBrAttr_ICEX_USR 0 +#undef AuBrAttr_ICEX_OTH +#define AuBrAttr_ICEX_OTH 0 +#endif +#endif + +/* the longest combination */ +/* AUFS_BRATTR_ICEX and AUFS_BRATTR_ICEX_TR don't affect here */ +#define AuBrPermStrSz sizeof(AUFS_BRPERM_RW \ + "+" AUFS_BRATTR_COO_REG \ + "+" AUFS_BRATTR_FHSM \ + "+" AUFS_BRATTR_UNPIN \ + "+" AUFS_BRATTR_ICEX_SEC \ + "+" AUFS_BRATTR_ICEX_SYS \ + "+" AUFS_BRATTR_ICEX_USR \ + "+" AUFS_BRATTR_ICEX_OTH \ + "+" AUFS_BRWATTR_NLWH) + +typedef struct { + char a[AuBrPermStrSz]; +} au_br_perm_str_t; + +static inline int au_br_writable(int brperm) +{ + return brperm & AuBrPerm_RW; +} + +static inline int au_br_whable(int brperm) +{ + return brperm & (AuBrPerm_RW | AuBrRAttr_WH); +} + +static inline int au_br_wh_linkable(int brperm) +{ + return !(brperm & AuBrWAttr_NoLinkWH); +} + +static inline int au_br_cmoo(int brperm) +{ + return brperm & AuBrAttr_CMOO_Mask; +} + +static inline int au_br_fhsm(int brperm) +{ + return brperm & AuBrAttr_FHSM; +} + +/* ---------------------------------------------------------------------- */ + +/* ioctl */ +enum { + /* readdir in userspace */ + AuCtl_RDU, + AuCtl_RDU_INO, + + AuCtl_WBR_FD, /* pathconf wrapper */ + AuCtl_IBUSY, /* busy inode */ + AuCtl_MVDOWN, /* move-down */ + AuCtl_BR, /* info about branches */ + AuCtl_FHSM_FD /* connection for fhsm */ +}; + +/* borrowed from linux/include/linux/kernel.h */ +#ifndef ALIGN +#ifdef _GNU_SOURCE +#define ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a)-1) +#else +#define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) +#endif +#define __ALIGN_MASK(x, mask) (((x)+(mask))&~(mask)) +#endif + +/* borrowed from linux/include/linux/compiler-gcc3.h */ +#ifndef __aligned +#define __aligned(x) __attribute__((aligned(x))) +#endif + +#ifdef __KERNEL__ +#ifndef __packed +#define __packed __attribute__((packed)) +#endif +#endif + +struct au_rdu_cookie { + uint64_t h_pos; + int16_t bindex; + uint8_t flags; + uint8_t pad; + uint32_t generation; +} __aligned(8); + +struct au_rdu_ent { + uint64_t ino; + int16_t bindex; + uint8_t type; + uint8_t nlen; + uint8_t wh; + char name[]; +} __aligned(8); + +static inline int au_rdu_len(int nlen) +{ + /* include the terminating NULL */ + return ALIGN(sizeof(struct au_rdu_ent) + nlen + 1, + sizeof(uint64_t)); +} + +union au_rdu_ent_ul { + struct au_rdu_ent __user *e; + uint64_t ul; +}; + +enum { + AufsCtlRduV_SZ, + AufsCtlRduV_End +}; + +struct aufs_rdu { + /* input */ + union { + uint64_t sz; /* AuCtl_RDU */ + uint64_t nent; /* AuCtl_RDU_INO */ + }; + union au_rdu_ent_ul ent; + uint16_t verify[AufsCtlRduV_End]; + + /* input/output */ + uint32_t blk; + + /* output */ + union au_rdu_ent_ul tail; + /* number of entries which were added in a single call */ + uint64_t rent; + uint8_t full; + uint8_t shwh; + + struct au_rdu_cookie cookie; +} __aligned(8); + +/* ---------------------------------------------------------------------- */ + +/* dirren. the branch is identified by the filename who contains this */ +struct au_drinfo { + uint64_t ino; + union { + uint8_t oldnamelen; + uint64_t _padding; + }; + uint8_t oldname[]; +} __aligned(8); + +struct au_drinfo_fdata { + uint32_t magic; + struct au_drinfo drinfo; +} __aligned(8); + +#define AUFS_DRINFO_MAGIC_V1 ('a' << 24 | 'd' << 16 | 'r' << 8 | 0x01) +/* future */ +#define AUFS_DRINFO_MAGIC_V2 ('a' << 24 | 'd' << 16 | 'r' << 8 | 0x02) + +/* ---------------------------------------------------------------------- */ + +struct aufs_wbr_fd { + uint32_t oflags; + int16_t brid; +} __aligned(8); + +/* ---------------------------------------------------------------------- */ + +struct aufs_ibusy { + uint64_t ino, h_ino; + int16_t bindex; +} __aligned(8); + +/* ---------------------------------------------------------------------- */ + +/* error code for move-down */ +/* the actual message strings are implemented in aufs-util.git */ +enum { + EAU_MVDOWN_OPAQUE = 1, + EAU_MVDOWN_WHITEOUT, + EAU_MVDOWN_UPPER, + EAU_MVDOWN_BOTTOM, + EAU_MVDOWN_NOUPPER, + EAU_MVDOWN_NOLOWERBR, + EAU_Last +}; + +/* flags for move-down */ +#define AUFS_MVDOWN_DMSG 1 +#define AUFS_MVDOWN_OWLOWER (1 << 1) /* overwrite lower */ +#define AUFS_MVDOWN_KUPPER (1 << 2) /* keep upper */ +#define AUFS_MVDOWN_ROLOWER (1 << 3) /* do even if lower is RO */ +#define AUFS_MVDOWN_ROLOWER_R (1 << 4) /* did on lower RO */ +#define AUFS_MVDOWN_ROUPPER (1 << 5) /* do even if upper is RO */ +#define AUFS_MVDOWN_ROUPPER_R (1 << 6) /* did on upper RO */ +#define AUFS_MVDOWN_BRID_UPPER (1 << 7) /* upper brid */ +#define AUFS_MVDOWN_BRID_LOWER (1 << 8) /* lower brid */ +#define AUFS_MVDOWN_FHSM_LOWER (1 << 9) /* find fhsm attr for lower */ +#define AUFS_MVDOWN_STFS (1 << 10) /* req. stfs */ +#define AUFS_MVDOWN_STFS_FAILED (1 << 11) /* output: stfs is unusable */ +#define AUFS_MVDOWN_BOTTOM (1 << 12) /* output: no more lowers */ + +/* index for move-down */ +enum { + AUFS_MVDOWN_UPPER, + AUFS_MVDOWN_LOWER, + AUFS_MVDOWN_NARRAY +}; + +/* + * additional info of move-down + * number of free blocks and inodes. + * subset of struct kstatfs, but smaller and always 64bit. + */ +struct aufs_stfs { + uint64_t f_blocks; + uint64_t f_bavail; + uint64_t f_files; + uint64_t f_ffree; +}; + +struct aufs_stbr { + int16_t brid; /* optional input */ + int16_t bindex; /* output */ + struct aufs_stfs stfs; /* output when AUFS_MVDOWN_STFS set */ +} __aligned(8); + +struct aufs_mvdown { + uint32_t flags; /* input/output */ + struct aufs_stbr stbr[AUFS_MVDOWN_NARRAY]; /* input/output */ + int8_t au_errno; /* output */ +} __aligned(8); + +/* ---------------------------------------------------------------------- */ + +union aufs_brinfo { + /* PATH_MAX may differ between kernel-space and user-space */ + char _spacer[4096]; + struct { + int16_t id; + int perm; + char path[]; + }; +} __aligned(8); + +/* ---------------------------------------------------------------------- */ + +#define AuCtlType 'A' +#define AUFS_CTL_RDU _IOWR(AuCtlType, AuCtl_RDU, struct aufs_rdu) +#define AUFS_CTL_RDU_INO _IOWR(AuCtlType, AuCtl_RDU_INO, struct aufs_rdu) +#define AUFS_CTL_WBR_FD _IOW(AuCtlType, AuCtl_WBR_FD, \ + struct aufs_wbr_fd) +#define AUFS_CTL_IBUSY _IOWR(AuCtlType, AuCtl_IBUSY, struct aufs_ibusy) +#define AUFS_CTL_MVDOWN _IOWR(AuCtlType, AuCtl_MVDOWN, \ + struct aufs_mvdown) +#define AUFS_CTL_BRINFO _IOW(AuCtlType, AuCtl_BR, union aufs_brinfo) +#define AUFS_CTL_FHSM_FD _IOW(AuCtlType, AuCtl_FHSM_FD, int) + +#endif /* __AUFS_TYPE_H__ */ From d094fec407af7066664f739dd4f128c8263bd933 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Fri, 3 Feb 2023 19:22:15 -0600 Subject: [PATCH 06/65] merge: aufs-rt Signed-off-by: Robert Nelson --- fs/aufs/i_op.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/aufs/i_op.c b/fs/aufs/i_op.c index d778b760c008d..12aa8155b0651 100644 --- a/fs/aufs/i_op.c +++ b/fs/aufs/i_op.c @@ -632,7 +632,11 @@ int au_pin_hdir_relock(struct au_pin *p) static void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task) { +#if IS_ENABLED(CONFIG_PREEMPT_RT) + p->hdir->hi_inode->i_rwsem.rtmutex.owner = task; +#else atomic_long_set(&p->hdir->hi_inode->i_rwsem.owner, (long)task); +#endif } void au_pin_hdir_acquire_nest(struct au_pin *p) From 203fad52cc656684a506c33da1f184b9652c5e32 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Fri, 3 Feb 2023 19:22:38 -0600 Subject: [PATCH 07/65] merge: wpanusb: https://git.beagleboard.org/beagleconnect/linux/wpanusb https://git.beagleboard.org/beagleconnect/linux/wpanusb/-/commit/6aa9bf65b9d88a2c9a111e7b4aed03de2be9413d Signed-off-by: Robert Nelson --- drivers/net/ieee802154/wpanusb.c | 778 +++++++++++++++++++++++++++++++ drivers/net/ieee802154/wpanusb.h | 53 +++ 2 files changed, 831 insertions(+) create mode 100644 drivers/net/ieee802154/wpanusb.c create mode 100644 drivers/net/ieee802154/wpanusb.h diff --git a/drivers/net/ieee802154/wpanusb.c b/drivers/net/ieee802154/wpanusb.c new file mode 100644 index 0000000000000..51eb0d32b291a --- /dev/null +++ b/drivers/net/ieee802154/wpanusb.c @@ -0,0 +1,778 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the WPANUSB IEEE 802.15.4 dongle + * + * Copyright (C) 2018 Intel Corp. + * + * The driver implements SoftMAC 802.15.4 protocol based on atusb + * driver for ATUSB IEEE 802.15.4 dongle. + * + * Written by Andrei Emeltchenko + */ + +#include +#include +#include +#include + +#include +#include + +#define DEBUG +#include "wpanusb.h" + +#define WPANUSB_NUM_RX_URBS 4 /* allow for a bit of local latency */ +#define WPANUSB_ALLOC_DELAY_MS 100 /* delay after failed allocation */ + +#define VENDOR_OUT (USB_TYPE_VENDOR | USB_DIR_OUT) +#define VENDOR_IN (USB_TYPE_VENDOR | USB_DIR_IN) + +#define WPANUSB_VALID_CHANNELS (0x07FFFFFF) + +struct wpanusb { + struct ieee802154_hw *hw; + struct usb_device *udev; + int shutdown; /* non-zero if shutting down */ + + /* RX variables */ + struct delayed_work work; /* memory allocations */ + struct usb_anchor idle_urbs; /* URBs waiting to be submitted */ + struct usb_anchor rx_urbs; /* URBs waiting for reception */ + + /* TX variables */ + struct usb_ctrlrequest tx_dr; + struct urb *tx_urb; + struct sk_buff *tx_skb; + u8 tx_ack_seq; /* current TX ACK sequence number */ +}; + +/* ----- USB commands without data ----------------------------------------- */ + +static int wpanusb_control_send(struct wpanusb *wpanusb, unsigned int pipe, + u8 request, void *data, u16 size) +{ + struct usb_device *udev = wpanusb->udev; + + return usb_control_msg(udev, pipe, request, VENDOR_OUT, + 0, 0, data, size, 1000); +} + +static int wpanusb_control_recv(struct wpanusb *wpanusb, u8 request, void *data, u16 size) +{ + struct usb_device *udev = wpanusb->udev; + + usb_control_msg(udev, usb_sndctrlpipe(udev, 0), request, VENDOR_OUT, + 0, 0, data, size, 1000); + + return usb_control_msg(udev, usb_rcvbulkpipe(udev, 1), request, VENDOR_IN, + 0, 0, data, size, 1000); +} + +/* ----- skb allocation ---------------------------------------------------- */ + +#define MAX_PSDU 127 +#define MAX_RX_XFER (1 + MAX_PSDU + 2 + 1) /* PHR+PSDU+CRC+LQI */ + +#define SKB_WPANUSB(skb) (*(struct wpanusb **)(skb)->cb) + +static void wpanusb_bulk_complete(struct urb *urb); + +static int wpanusb_submit_rx_urb(struct wpanusb *wpanusb, struct urb *urb) +{ + struct usb_device *udev = wpanusb->udev; + struct sk_buff *skb = urb->context; + int ret; + + if (!skb) { + skb = alloc_skb(MAX_RX_XFER, GFP_KERNEL); + if (!skb) { + dev_warn_ratelimited(&udev->dev, + "can't allocate skb\n"); + return -ENOMEM; + } + skb_put(skb, MAX_RX_XFER); + SKB_WPANUSB(skb) = wpanusb; + } + + usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, 1), + skb->data, MAX_RX_XFER, wpanusb_bulk_complete, skb); + usb_anchor_urb(urb, &wpanusb->rx_urbs); + + ret = usb_submit_urb(urb, GFP_KERNEL); + if (ret) { + usb_unanchor_urb(urb); + kfree_skb(skb); + urb->context = NULL; + } + + return ret; +} + +static void wpanusb_work_urbs(struct work_struct *work) +{ + struct wpanusb *wpanusb = + container_of(to_delayed_work(work), struct wpanusb, work); + struct usb_device *udev = wpanusb->udev; + struct urb *urb; + int ret; + + if (wpanusb->shutdown) + return; + + do { + urb = usb_get_from_anchor(&wpanusb->idle_urbs); + if (!urb) + return; + + ret = wpanusb_submit_rx_urb(wpanusb, urb); + } while (!ret); + + usb_anchor_urb(urb, &wpanusb->idle_urbs); + dev_warn_ratelimited(&udev->dev, "can't allocate/submit URB (%d)\n", + ret); + schedule_delayed_work(&wpanusb->work, + msecs_to_jiffies(WPANUSB_ALLOC_DELAY_MS) + 1); +} + +/* ----- Asynchronous USB -------------------------------------------------- */ + +static void wpanusb_tx_done(struct wpanusb *wpanusb, uint8_t seq) +{ + struct usb_device *udev = wpanusb->udev; + u8 expect = wpanusb->tx_ack_seq; + + dev_dbg(&udev->dev, "seq 0x%02x expect 0x%02x\n", seq, expect); + + if (seq == expect) { + ieee802154_xmit_complete(wpanusb->hw, wpanusb->tx_skb, false); + } else { + dev_dbg(&udev->dev, "unknown ack %u\n", seq); + + ieee802154_wake_queue(wpanusb->hw); + if (wpanusb->tx_skb) + dev_kfree_skb_irq(wpanusb->tx_skb); + } +} + +static void wpanusb_process_urb(struct urb *urb) +{ + struct usb_device *udev = urb->dev; + struct sk_buff *skb = urb->context; + struct wpanusb *wpanusb = SKB_WPANUSB(skb); + u8 len, lqi; + + if (!urb->actual_length) { + dev_dbg(&udev->dev, "zero-sized URB ?\n"); + return; + } + + len = *skb->data; + + dev_dbg(&udev->dev, "urb %p urb len %u pkt len %u", urb, + urb->actual_length, len); + + /* Handle ACK */ + if (urb->actual_length == 1) { + wpanusb_tx_done(wpanusb, len); + return; + } + + if (len + 1 > urb->actual_length - 1) { + dev_dbg(&udev->dev, "frame len %d+1 > URB %u-1\n", + len, urb->actual_length); + return; + } + + if (!ieee802154_is_valid_psdu_len(len)) { + dev_dbg(&udev->dev, "frame corrupted\n"); + return; + } + + print_hex_dump_bytes("> ", DUMP_PREFIX_OFFSET, skb->data, + urb->actual_length); + + /* Get LQI at the end of the packet */ + lqi = skb->data[len + 1]; + dev_dbg(&udev->dev, "rx len %d lqi 0x%02x\n", len, lqi); + skb_pull(skb, 1); /* remove length */ + skb_trim(skb, len); /* remove LQI */ + ieee802154_rx_irqsafe(wpanusb->hw, skb, lqi); + urb->context = NULL; /* skb is gone */ +} + +static void wpanusb_bulk_complete(struct urb *urb) +{ + struct usb_device *udev = urb->dev; + struct sk_buff *skb = urb->context; + struct wpanusb *wpanusb = SKB_WPANUSB(skb); + + dev_dbg(&udev->dev, "status %d len %d\n", + urb->status, urb->actual_length); + + if (urb->status) { + if (urb->status == -ENOENT) { /* being killed */ + kfree_skb(skb); + urb->context = NULL; + return; + } + + dev_dbg(&udev->dev, "URB error %d\n", urb->status); + } else { + wpanusb_process_urb(urb); + } + + usb_anchor_urb(urb, &wpanusb->idle_urbs); + if (!wpanusb->shutdown) + schedule_delayed_work(&wpanusb->work, 0); +} + +/* ----- URB allocation/deallocation --------------------------------------- */ + +static void wpanusb_free_urbs(struct wpanusb *wpanusb) +{ + struct urb *urb; + + do { + urb = usb_get_from_anchor(&wpanusb->idle_urbs); + if (!urb) + break; + kfree_skb(urb->context); + usb_free_urb(urb); + } while (true); +} + +static int wpanusb_alloc_urbs(struct wpanusb *wpanusb, unsigned int n) +{ + struct urb *urb; + + while (n--) { + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + wpanusb_free_urbs(wpanusb); + return -ENOMEM; + } + usb_anchor_urb(urb, &wpanusb->idle_urbs); + } + + return 0; +} + +/* ----- IEEE 802.15.4 interface operations -------------------------------- */ + +static void wpanusb_xmit_complete(struct urb *urb) +{ + dev_dbg(&urb->dev->dev, "urb transmit completed"); +} + +static int wpanusb_xmit(struct ieee802154_hw *hw, struct sk_buff *skb) +{ + struct wpanusb *wpanusb = hw->priv; + struct usb_device *udev = wpanusb->udev; + int ret = 0; + + dev_dbg(&udev->dev, "len %u", skb->len); + + /* ack_seq range is 0x01 - 0xff */ + wpanusb->tx_ack_seq++; + if (!wpanusb->tx_ack_seq) + wpanusb->tx_ack_seq++; + + wpanusb->tx_skb = skb; + wpanusb->tx_dr.wIndex = cpu_to_le16(wpanusb->tx_ack_seq); + wpanusb->tx_dr.wLength = cpu_to_le16(skb->len); + + usb_fill_control_urb(wpanusb->tx_urb, udev, + usb_sndctrlpipe(udev, 0), + (unsigned char *)&wpanusb->tx_dr, skb->data, + skb->len, wpanusb_xmit_complete, NULL); + ret = usb_submit_urb(wpanusb->tx_urb, GFP_ATOMIC); + + dev_dbg(&udev->dev, "%s: ret %d len %u seq %u\n", __func__, ret, + skb->len, wpanusb->tx_ack_seq); + + return ret; +} + +static int wpanusb_channel(struct ieee802154_hw *hw, u8 page, u8 channel) +{ + struct wpanusb *wpanusb = hw->priv; + struct usb_device *udev = wpanusb->udev; + struct set_channel *req; + int ret; + + req = kmalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + req->page = page; + req->channel = channel; + + ret = wpanusb_control_send(wpanusb, usb_sndctrlpipe(udev, 0), + SET_CHANNEL, req, sizeof(*req)); + kfree(req); + if (ret < 0) { + dev_err(&udev->dev, "Failed set channel, ret %d", ret); + return ret; + } + + dev_dbg(&udev->dev, "set page %u channel %u", page, channel); + + return 0; +} + +static int wpanusb_ed(struct ieee802154_hw *hw, u8 *level) +{ + WARN_ON(!level); + + *level = 0xbe; + + return 0; +} + +static int wpanusb_set_hw_addr_filt(struct ieee802154_hw *hw, + struct ieee802154_hw_addr_filt *filt, + unsigned long changed) +{ + struct wpanusb *wpanusb = hw->priv; + struct usb_device *udev = wpanusb->udev; + int ret = 0; + + if (changed & IEEE802154_AFILT_SADDR_CHANGED) { + struct set_short_addr *req; + + req = kmalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + req->short_addr = filt->short_addr; + + ret = wpanusb_control_send(wpanusb, usb_sndctrlpipe(udev, 0), + SET_SHORT_ADDR, req, sizeof(*req)); + kfree(req); + if (ret < 0) { + dev_err(&udev->dev, "Failed to set short_addr, ret %d", + ret); + return ret; + } + + dev_dbg(&udev->dev, "short addr changed to 0x%04x", + le16_to_cpu(filt->short_addr)); + } + + if (changed & IEEE802154_AFILT_PANID_CHANGED) { + struct set_pan_id *req; + + req = kmalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + req->pan_id = filt->pan_id; + + ret = wpanusb_control_send(wpanusb, usb_sndctrlpipe(udev, 0), + SET_PAN_ID, req, sizeof(*req)); + kfree(req); + if (ret < 0) { + dev_err(&udev->dev, "Failed to set pan_id, ret %d", + ret); + return ret; + } + + dev_dbg(&udev->dev, "pan id changed to 0x%04x", + le16_to_cpu(filt->pan_id)); + } + + if (changed & IEEE802154_AFILT_IEEEADDR_CHANGED) { + struct set_ieee_addr *req; + + req = kmalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + memcpy(&req->ieee_addr, &filt->ieee_addr, + sizeof(req->ieee_addr)); + + ret = wpanusb_control_send(wpanusb, usb_sndctrlpipe(udev, 0), + SET_IEEE_ADDR, req, sizeof(*req)); + kfree(req); + if (ret < 0) { + dev_err(&udev->dev, "Failed to set ieee_addr, ret %d", + ret); + return ret; + } + + dev_dbg(&udev->dev, "IEEE addr changed"); + } + + if (changed & IEEE802154_AFILT_PANC_CHANGED) { + dev_dbg(&udev->dev, "panc changed"); + + dev_err(&udev->dev, "Not handled AFILT_PANC_CHANGED"); + } + + return ret; +} + +static int wpanusb_set_extended_addr(struct ieee802154_hw *hw) +{ + struct wpanusb *wpanusb = hw->priv; + struct usb_device *udev = wpanusb->udev; + unsigned char *buffer; + __le64 extended_addr; + int ret = 0; + u64 addr; + + buffer = kmalloc(IEEE802154_EXTENDED_ADDR_LEN, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + ret = wpanusb_control_send(wpanusb, usb_sndctrlpipe(udev, 0), GET_EXTENDED_ADDR, buffer, + IEEE802154_EXTENDED_ADDR_LEN); + if (ret < 0) { + dev_err(&udev->dev, "failed to fetch extended address, random address set\n"); + ieee802154_random_extended_addr(&wpanusb->hw->phy->perm_extended_addr); + kfree(buffer); + return ret; + } + + memcpy(&extended_addr, buffer, IEEE802154_EXTENDED_ADDR_LEN); + /* Check if read address is not empty and the unicast bit is set correctly */ + if (!ieee802154_is_valid_extended_unicast_addr(extended_addr)) { + dev_info(&udev->dev, "no permanent extended address found, random address set\n"); + ieee802154_random_extended_addr(&wpanusb->hw->phy->perm_extended_addr); + } else { + wpanusb->hw->phy->perm_extended_addr = extended_addr; + addr = swab64((__force u64)wpanusb->hw->phy->perm_extended_addr); + dev_info(&udev->dev, "Read permanent extended address %8phC from device\n", &addr); + } + + kfree(buffer); + return ret; +} + +/* FIXME: these need to come as capabilities from the device */ +static const s32 wpanusb_powers[] = { + 300, 280, 230, 180, 130, 70, 0, -100, -200, -300, -400, -500, -700, + -900, -1200, -1700, +}; + +static int wpanusb_get_device_capabilities(struct ieee802154_hw *hw) +{ + struct wpanusb *wpanusb = hw->priv; + struct usb_device *udev = wpanusb->udev; + unsigned char *buffer; + uint32_t valid_channels; + int ret = 0; + + buffer = kmalloc(IEEE802154_EXTENDED_ADDR_LEN, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + ret = wpanusb_control_send(wpanusb, usb_sndctrlpipe(udev, 0), GET_EXTENDED_ADDR, buffer, + IEEE802154_EXTENDED_ADDR_LEN); + if (ret < 0) { + dev_err(&udev->dev, "failed to fetch extended address, random address set\n"); + ieee802154_random_extended_addr(&wpanusb->hw->phy->perm_extended_addr); + kfree(buffer); + return ret; + } + + buffer = kmalloc(sizeof(valid_channels), GFP_NOIO); + if (!buffer) + return -ENOMEM; + ret = wpanusb_control_recv(wpanusb, GET_SUPPORTED_CHANNELS, buffer, sizeof(valid_channels)); + valid_channels = *(uint32_t *)buffer; + if (ret < 0 || !valid_channels) { + dev_err(&udev->dev, "failed to fetch valid channels, setting default valid channels\n"); + valid_channels = WPANUSB_VALID_CHANNELS; + } + + /* FIXME: these need to come from device capabilities */ + hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT; + + /* FIXME: these need to come from device capabilities */ + hw->phy->flags = WPAN_PHY_FLAG_TXPOWER; + + /* Set default and supported channels */ + hw->phy->current_page = 0; + hw->phy->current_channel = ffs(valid_channels) - 1; //set to lowest valid channel + hw->phy->supported.channels[0] = valid_channels; + + /* FIXME: these need to come from device capabilities */ + hw->phy->supported.tx_powers = wpanusb_powers; + hw->phy->supported.tx_powers_size = ARRAY_SIZE(wpanusb_powers); + hw->phy->transmit_power = hw->phy->supported.tx_powers[0]; + + kfree(buffer); + return ret; +} + +static int wpanusb_start(struct ieee802154_hw *hw) +{ + struct wpanusb *wpanusb = hw->priv; + struct usb_device *udev = wpanusb->udev; + int ret; + + schedule_delayed_work(&wpanusb->work, 0); + + ret = wpanusb_control_send(wpanusb, usb_sndctrlpipe(udev, 0), + START, NULL, 0); + if (ret < 0) { + dev_err(&udev->dev, "Failed to start ieee802154"); + usb_kill_anchored_urbs(&wpanusb->idle_urbs); + } + + return ret; +} + +static void wpanusb_stop(struct ieee802154_hw *hw) +{ + struct wpanusb *wpanusb = hw->priv; + struct usb_device *udev = wpanusb->udev; + int ret; + + dev_dbg(&udev->dev, "stop"); + + usb_kill_anchored_urbs(&wpanusb->idle_urbs); + + ret = wpanusb_control_send(wpanusb, usb_sndctrlpipe(udev, 0), + STOP, NULL, 0); + if (ret < 0) + dev_err(&udev->dev, "Failed to stop ieee802154"); +} + +static int wpanusb_set_txpower(struct ieee802154_hw *hw, s32 mbm) +{ + struct wpanusb *wpanusb = hw->priv; + struct usb_device *udev = wpanusb->udev; + + dev_err(&udev->dev, "%s: Not handled, mbm %d", __func__, mbm); + + return -ENOTSUPP; +} + +static int wpanusb_set_cca_mode(struct ieee802154_hw *hw, + const struct wpan_phy_cca *cca) +{ + struct wpanusb *wpanusb = hw->priv; + struct usb_device *udev = wpanusb->udev; + + dev_err(&udev->dev, "%s: Not handled, mode %u opt %u", + __func__, cca->mode, cca->opt); + + switch (cca->mode) { + case NL802154_CCA_ENERGY: + break; + case NL802154_CCA_CARRIER: + break; + case NL802154_CCA_ENERGY_CARRIER: + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wpanusb_set_lbt(struct ieee802154_hw *hw, bool on) +{ + struct wpanusb *wpanusb = hw->priv; + struct usb_device *udev = wpanusb->udev; + int ret = 0; + + if (on) + ret = wpanusb_control_send(wpanusb, usb_sndctrlpipe(udev, 0), + SET_LBT, NULL, 0); + + return ret; +} + +static int wpanusb_set_frame_retries(struct ieee802154_hw *hw, s8 retries) +{ + struct wpanusb *wpanusb = hw->priv; + struct usb_device *udev = wpanusb->udev; + int ret; + + /* FIXME pass retries onwards to device */ + ret = wpanusb_control_send(wpanusb, usb_sndctrlpipe(udev, 0), + SET_FRAME_RETRIES, NULL, 0); + + return ret; +} + +static int wpanusb_set_cca_ed_level(struct ieee802154_hw *hw, s32 mbm) +{ + struct wpanusb *wpanusb = hw->priv; + struct usb_device *udev = wpanusb->udev; + + dev_err(&udev->dev, "%s: Not handled, mbm %d", __func__, mbm); + + return 0; +} + +static int wpanusb_set_csma_params(struct ieee802154_hw *hw, u8 min_be, + u8 max_be, u8 retries) +{ + struct wpanusb *wpanusb = hw->priv; + struct usb_device *udev = wpanusb->udev; + + dev_err(&udev->dev, "%s: Not handled, min_be %u max_be %u retr %u", + __func__, min_be, max_be, retries); + + return 0; +} + +static int wpanusb_set_promiscuous_mode(struct ieee802154_hw *hw, const bool on) +{ + struct wpanusb *wpanusb = hw->priv; + struct usb_device *udev = wpanusb->udev; + + dev_err(&udev->dev, "%s: Not handled, on %d", __func__, on); + + return 0; +} + +static const struct ieee802154_ops wpanusb_ops = { + .owner = THIS_MODULE, + .xmit_async = wpanusb_xmit, + .ed = wpanusb_ed, + .set_channel = wpanusb_channel, + .start = wpanusb_start, + .stop = wpanusb_stop, + .set_hw_addr_filt = wpanusb_set_hw_addr_filt, + .set_txpower = wpanusb_set_txpower, + .set_lbt = wpanusb_set_lbt, + .set_cca_mode = wpanusb_set_cca_mode, + .set_cca_ed_level = wpanusb_set_cca_ed_level, + .set_csma_params = wpanusb_set_csma_params, + .set_frame_retries = wpanusb_set_frame_retries, + .set_promiscuous_mode = wpanusb_set_promiscuous_mode, +}; + +/* ----- Setup ------------------------------------------------------------- */ + +static int wpanusb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct ieee802154_hw *hw; + struct wpanusb *wpanusb; + int ret; + + hw = ieee802154_alloc_hw(sizeof(struct wpanusb), &wpanusb_ops); + if (!hw) + return -ENOMEM; + + wpanusb = hw->priv; + wpanusb->hw = hw; + wpanusb->udev = usb_get_dev(udev); + usb_set_intfdata(interface, wpanusb); + + wpanusb->shutdown = 0; + INIT_DELAYED_WORK(&wpanusb->work, wpanusb_work_urbs); + init_usb_anchor(&wpanusb->idle_urbs); + init_usb_anchor(&wpanusb->rx_urbs); + + ret = wpanusb_alloc_urbs(wpanusb, WPANUSB_NUM_RX_URBS); + if (ret) + goto fail; + + wpanusb->tx_dr.bRequestType = VENDOR_OUT; + wpanusb->tx_dr.bRequest = TX; + wpanusb->tx_dr.wValue = cpu_to_le16(0); + + wpanusb->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!wpanusb->tx_urb) + goto fail; + + hw->parent = &udev->dev; + + ret = wpanusb_control_send(wpanusb, usb_sndctrlpipe(udev, 0), RESET, + NULL, 0); + if (ret < 0) { + dev_err(&udev->dev, "Failed to RESET ieee802154"); + goto fail; + } + + ret = wpanusb_get_device_capabilities(hw); + + if (ret < 0) { + dev_err(&udev->dev, "Failed to get device capabilities"); + goto fail; + } + + ret = wpanusb_set_extended_addr(hw); + + if (ret < 0) { + dev_err(&udev->dev, "Failed to set permanent address"); + goto fail; + } + + ret = ieee802154_register_hw(hw); + if (ret) { + dev_err(&udev->dev, "Failed to register ieee802154"); + goto fail; + } + + dev_dbg(&udev->dev, "ieee802154 ready to go"); + + return 0; + +fail: + dev_err(&udev->dev, "Failed ieee802154 probe"); + wpanusb_free_urbs(wpanusb); + usb_kill_urb(wpanusb->tx_urb); + usb_free_urb(wpanusb->tx_urb); + usb_put_dev(udev); + ieee802154_free_hw(hw); + + return ret; +} + +static void wpanusb_disconnect(struct usb_interface *interface) +{ + struct wpanusb *wpanusb = usb_get_intfdata(interface); + + wpanusb->shutdown = 1; + cancel_delayed_work_sync(&wpanusb->work); + + usb_kill_anchored_urbs(&wpanusb->rx_urbs); + wpanusb_free_urbs(wpanusb); + usb_kill_urb(wpanusb->tx_urb); + usb_free_urb(wpanusb->tx_urb); + + ieee802154_unregister_hw(wpanusb->hw); + + ieee802154_free_hw(wpanusb->hw); + + usb_set_intfdata(interface, NULL); + usb_put_dev(wpanusb->udev); +} + +/* The devices we work with */ +static const struct usb_device_id wpanusb_device_table[] = { + { + USB_DEVICE_AND_INTERFACE_INFO(WPANUSB_VENDOR_ID, + WPANUSB_PRODUCT_ID, + USB_CLASS_VENDOR_SPEC, + 0, 0), + USB_DEVICE_AND_INTERFACE_INFO(BEAGLECONNECT_VENDOR_ID, + BEAGLECONNECT_PRODUCT_ID, + USB_CLASS_VENDOR_SPEC, + 0, 0) + }, + /* end with null element */ + {} +}; +MODULE_DEVICE_TABLE(usb, wpanusb_device_table); + +static struct usb_driver wpanusb_driver = { + .name = "wpanusb", + .probe = wpanusb_probe, + .disconnect = wpanusb_disconnect, + .id_table = wpanusb_device_table, +}; +module_usb_driver(wpanusb_driver); + +MODULE_AUTHOR("Andrei Emeltchenko "); +MODULE_DESCRIPTION("WPANUSB IEEE 802.15.4 over USB Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ieee802154/wpanusb.h b/drivers/net/ieee802154/wpanusb.h new file mode 100644 index 0000000000000..52e54fdca737e --- /dev/null +++ b/drivers/net/ieee802154/wpanusb.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Definitions shared between kernel and WPANUSB firmware + * + * Copyright (C) 2018 Intel Corp. + * + * Written by Andrei Emeltchenko + */ + +#define WPANUSB_VENDOR_ID 0x2fe3 +#define WPANUSB_PRODUCT_ID 0x0101 + +#define BEAGLECONNECT_VENDOR_ID 0x2047 +#define BEAGLECONNECT_PRODUCT_ID 0x0aa5 + +enum wpanusb_requests { + RESET, + TX, + XMIT_ASYNC, + ED, + SET_CHANNEL, + START, + STOP, + SET_SHORT_ADDR, + SET_PAN_ID, + SET_IEEE_ADDR, + SET_TXPOWER, + SET_CCA_MODE, + SET_CCA_ED_LEVEL, + SET_CSMA_PARAMS, + SET_LBT, + SET_FRAME_RETRIES, + SET_PROMISCUOUS_MODE, + GET_EXTENDED_ADDR, + GET_SUPPORTED_CHANNELS, +}; + +struct set_channel { + __u8 page; + __u8 channel; +} __packed; + +struct set_short_addr { + __le16 short_addr; +} __packed; + +struct set_pan_id { + __le16 pan_id; +} __packed; + +struct set_ieee_addr { + __le64 ieee_addr; +} __packed; From 82fb6221e49d0ffe066a94399de40b913dedeca1 Mon Sep 17 00:00:00 2001 From: Jason Kridner Date: Sun, 3 Jan 2021 17:43:36 -0500 Subject: [PATCH 08/65] Add WPANUSB driver From https://github.com/statropy/wpanusb --- drivers/net/ieee802154/Kconfig | 9 +++++++++ drivers/net/ieee802154/Makefile | 1 + 2 files changed, 10 insertions(+) diff --git a/drivers/net/ieee802154/Kconfig b/drivers/net/ieee802154/Kconfig index 0f7c6dc2ed154..0a1d87691d50c 100644 --- a/drivers/net/ieee802154/Kconfig +++ b/drivers/net/ieee802154/Kconfig @@ -73,6 +73,15 @@ config IEEE802154_ATUSB This driver can also be built as a module. To do so say M here. The module will be called 'atusb'. +config IEEE802154_WPANUSB + tristate "WPANUSB driver" + depends on IEEE802154_DRIVERS && MAC802154 && USB + help + Adds support for WPANUSB 802.15.4 adapters. + + This driver should work with at least the following devices: + * BeagleBoard.org BeagleConnect Freedom + config IEEE802154_ADF7242 tristate "ADF7242 transceiver driver" depends on IEEE802154_DRIVERS && MAC802154 diff --git a/drivers/net/ieee802154/Makefile b/drivers/net/ieee802154/Makefile index 0c78b62980606..59442aab94038 100644 --- a/drivers/net/ieee802154/Makefile +++ b/drivers/net/ieee802154/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_IEEE802154_AT86RF230) += at86rf230.o obj-$(CONFIG_IEEE802154_MRF24J40) += mrf24j40.o obj-$(CONFIG_IEEE802154_CC2520) += cc2520.o obj-$(CONFIG_IEEE802154_ATUSB) += atusb.o +obj-$(CONFIG_IEEE802154_WPANUSB) += wpanusb.o obj-$(CONFIG_IEEE802154_ADF7242) += adf7242.o obj-$(CONFIG_IEEE802154_CA8210) += ca8210.o obj-$(CONFIG_IEEE802154_MCR20A) += mcr20a.o From a9ff927079bfa633e0c13b24b915a8f68dc8781a Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Fri, 3 Feb 2023 19:23:04 -0600 Subject: [PATCH 09/65] merge: bcfserial: https://git.beagleboard.org/beagleconnect/linux/bcfserial.git https://git.beagleboard.org/beagleconnect/linux/bcfserial/-/commit/db467023bd136c97c2e13c3a8b9e41dbdfafbc66 Signed-off-by: Robert Nelson --- drivers/net/ieee802154/bcfserial.c | 680 +++++++++++++++++++++++++++++ 1 file changed, 680 insertions(+) create mode 100644 drivers/net/ieee802154/bcfserial.c diff --git a/drivers/net/ieee802154/bcfserial.c b/drivers/net/ieee802154/bcfserial.c new file mode 100644 index 0000000000000..11a456e56887e --- /dev/null +++ b/drivers/net/ieee802154/bcfserial.c @@ -0,0 +1,680 @@ + +/* + * bcfserial.c - Serial interface driver for BeagleConnect Freedom. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DEBUG + +#define BCFSERIAL_DRV_VERSION "0.1.0" +#define BCFSERIAL_DRV_NAME "bcfserial" + +#define HDLC_FRAME 0x7E +#define HDLC_ESC 0x7D +#define HDLC_XOR 0x20 + +#define ADDRESS_CTRL 0x01 +#define ADDRESS_WPAN 0x03 +#define ADDRESS_CDC 0x05 +#define ADDRESS_HW 0x41 + +#define MAX_PSDU 127 +#define MAX_RX_XFER (1 + MAX_PSDU + 2 + 1) /* PHR+PSDU+CRC+LQI */ +#define HDLC_HEADER_LEN 2 +#define PACKET_HEADER_LEN 8 +#define CRC_LEN 2 +#define RX_HDLC_PAYLOAD 140 +#define MAX_TX_HDLC (1 + HDLC_HEADER_LEN + PACKET_HEADER_LEN + MAX_RX_XFER + CRC_LEN + 1) +#define MAX_RX_HDLC (1 + RX_HDLC_PAYLOAD + CRC_LEN) +#define TX_CIRC_BUF_SIZE 1024 + +enum bcfserial_requests { + RESET, + TX, + XMIT_ASYNC, + ED, + SET_CHANNEL, + START, + STOP, + SET_SHORT_ADDR, + SET_PAN_ID, + SET_IEEE_ADDR, + SET_TXPOWER, + SET_CCA_MODE, + SET_CCA_ED_LEVEL, + SET_CSMA_PARAMS, + SET_LBT, + SET_FRAME_RETRIES, + SET_PROMISCUOUS_MODE, + GET_EXTENDED_ADDR, + GET_SUPPORTED_CHANNELS, +}; + +struct bcfserial { + struct serdev_device *serdev; + struct ieee802154_hw *hw; + + struct work_struct tx_work; + spinlock_t tx_producer_lock; + spinlock_t tx_consumer_lock; + struct circ_buf tx_circ_buf; + struct sk_buff *tx_skb; + u16 tx_crc; + u8 tx_ack_seq; /* current TX ACK sequence number */ + + size_t response_size; + u8 *response_buffer; + + u8 rx_in_esc; + u8 rx_address; + u16 rx_offset; + u8 *rx_buffer; +}; + +// RX Packet Format: +// - WPAN RX PACKET: [len] payload [lqi] +// - WPAN TX ACK: [seq] +// - WPAN CAPABILITIES: supported_channels_mask(4) +// - CDC: printable_chars + +static void bcfserial_serdev_write_locked(struct bcfserial *bcfserial) +{ + //must be locked already + int head = smp_load_acquire(&bcfserial->tx_circ_buf.head); + int tail = bcfserial->tx_circ_buf.tail; + int count = CIRC_CNT_TO_END(head, tail, TX_CIRC_BUF_SIZE); + int written; + + if (count >= 1) { + written = serdev_device_write_buf(bcfserial->serdev, &bcfserial->tx_circ_buf.buf[tail], count); + + smp_store_release(&(bcfserial->tx_circ_buf.tail), (tail + written) & (TX_CIRC_BUF_SIZE - 1)); + } +} + +static void bcfserial_append(struct bcfserial *bcfserial, u8 value) +{ + //must be locked already + int head = bcfserial->tx_circ_buf.head; + + while(true) + { + int tail = READ_ONCE(bcfserial->tx_circ_buf.tail); + + if (CIRC_SPACE(head, tail, TX_CIRC_BUF_SIZE) >= 1) { + + bcfserial->tx_circ_buf.buf[head] = value; + + smp_store_release(&(bcfserial->tx_circ_buf.head), + (head + 1) & (TX_CIRC_BUF_SIZE - 1)); + return; + } else { + dev_dbg(&bcfserial->serdev->dev, "Tx circ buf full\n"); + usleep_range(3000,5000); + } + } +} + +static void bcfserial_append_tx_frame(struct bcfserial *bcfserial) +{ + bcfserial->tx_crc = 0xFFFF; + bcfserial_append(bcfserial, HDLC_FRAME); +} + +static void bcfserial_append_escaped(struct bcfserial *bcfserial, u8 value) +{ + if (value == HDLC_FRAME || value == HDLC_ESC) { + bcfserial_append(bcfserial, HDLC_ESC); + value ^= HDLC_XOR; + } + bcfserial_append(bcfserial, value); +} + +static void bcfserial_append_tx_u8(struct bcfserial *bcfserial, u8 value) +{ + bcfserial->tx_crc = crc_ccitt(bcfserial->tx_crc, &value, 1); + bcfserial_append_escaped(bcfserial, value); +} + +static void bcfserial_append_tx_buffer(struct bcfserial *bcfserial, const void *buffer, size_t len) +{ + size_t i; + for (i=0; itx_crc ^= 0xffff; + bcfserial_append_escaped(bcfserial, bcfserial->tx_crc & 0xff); + bcfserial_append_escaped(bcfserial, (bcfserial->tx_crc >> 8) & 0xff); +} + +static void bcfserial_hdlc_send(struct bcfserial *bcfserial, u8 cmd, u16 value, u16 index, u16 length, const void* buffer) +{ + // HDLC_FRAME + // 0 address : 0x01 + // 1 control : 0x03 + // 2 [bmRequestType] : 0x00 + // 3 cmd (TX, START, STOP, etc) + // 4/5 value + // 6/7 index + // 8/9 length + // contents + // x/y crc + // HDLC_FRAME + + spin_lock(&bcfserial->tx_producer_lock); + + bcfserial_append_tx_frame(bcfserial); + bcfserial_append_tx_u8(bcfserial, 0x01); //address + bcfserial_append_tx_u8(bcfserial, 0x03); //control + bcfserial_append_tx_u8(bcfserial, 0x00); //ignored + bcfserial_append_tx_u8(bcfserial, cmd); + bcfserial_append_tx_le16(bcfserial, value); + bcfserial_append_tx_le16(bcfserial, index); + bcfserial_append_tx_le16(bcfserial, length); + bcfserial_append_tx_buffer(bcfserial, buffer, length); + bcfserial_append_tx_crc(bcfserial); + bcfserial_append_tx_frame(bcfserial); + + spin_unlock(&bcfserial->tx_producer_lock); + + spin_lock(&bcfserial->tx_consumer_lock); + bcfserial_serdev_write_locked(bcfserial); + spin_unlock(&bcfserial->tx_consumer_lock); +} + +static void bcfserial_hdlc_send_cmd(struct bcfserial *bcfserial, u8 cmd) +{ + bcfserial_hdlc_send(bcfserial, cmd, 0, 0, 0, NULL); +} + +static void bcfserial_hdlc_send_ack(struct bcfserial *bcfserial, u8 address, u8 seq) +{ + // To make this a valid S-frame: + // u8 ctrl = (((seq + 1) & 0x07) << 5) | 0x01; + // TODO Fix control frame type bug here and in wpanusb_bc + + spin_lock(&bcfserial->tx_producer_lock); + + bcfserial_append_tx_frame(bcfserial); + bcfserial_append_tx_u8(bcfserial, address); //address + bcfserial_append_tx_u8(bcfserial, 0x00); //control + bcfserial_append_tx_crc(bcfserial); + bcfserial_append_tx_frame(bcfserial); + + spin_unlock(&bcfserial->tx_producer_lock); + + spin_lock(&bcfserial->tx_consumer_lock); + bcfserial_serdev_write_locked(bcfserial); + spin_unlock(&bcfserial->tx_consumer_lock); +} + +static int bcfserial_hdlc_receive(struct bcfserial *bcfserial, u8 cmd, void *buffer, size_t count) +{ + int retries = 80; + bcfserial->response_size = count; + bcfserial->response_buffer = (u8*)buffer; + bcfserial_hdlc_send_cmd(bcfserial, cmd); + // TODO semaphore? give/take + do { + usleep_range(10000,10001); + } while (bcfserial->response_size && retries--); + bcfserial->response_buffer = NULL; + if (bcfserial->response_size) { + bcfserial->response_size = 0; + return -EAGAIN; + } + return 0; +} + +static int bcfserial_start(struct ieee802154_hw *hw) +{ + struct bcfserial *bcfserial = hw->priv; + dev_dbg(&bcfserial->serdev->dev, "START\n"); + bcfserial_hdlc_send_cmd(bcfserial, START); + return 0; +} + +static void bcfserial_stop(struct ieee802154_hw *hw) +{ + struct bcfserial *bcfserial = hw->priv; + dev_dbg(&bcfserial->serdev->dev, "STOP\n"); + bcfserial_hdlc_send_cmd(bcfserial, STOP); +} + +static int bcfserial_xmit(struct ieee802154_hw *hw, struct sk_buff *skb) +{ + struct bcfserial *bcfserial = hw->priv; + + if (bcfserial->tx_skb) + { + dev_err(&bcfserial->serdev->dev, "SKB not freed! %d\n", bcfserial->tx_ack_seq); + } + + bcfserial->tx_skb = skb; + bcfserial->tx_ack_seq++; + if (!bcfserial->tx_ack_seq) { + bcfserial->tx_ack_seq++; + } + + dev_dbg(&bcfserial->serdev->dev, "XMIT %02x %d\n", bcfserial->tx_ack_seq, skb->len); + + bcfserial_hdlc_send(bcfserial, TX, 0, bcfserial->tx_ack_seq, skb->len, skb->data); + + return 0; +} + +static int bcfserial_ed(struct ieee802154_hw *hw, u8 *level) +{ + struct bcfserial *bcfserial = hw->priv; + dev_dbg(&bcfserial->serdev->dev, "ED\n"); + WARN_ON(!level); + *level = 0xbe; + return 0; +} + +static int bcfserial_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel) +{ + struct bcfserial *bcfserial = hw->priv; + u8 buffer[2] = {page, channel}; + dev_dbg(&bcfserial->serdev->dev, "SET CHANNEL %u %u\n", page, channel); + bcfserial_hdlc_send(bcfserial, SET_CHANNEL, 0, 0, 2, &buffer); + return 0; +} + +static int bcfserial_set_hw_addr_filt(struct ieee802154_hw *hw, + struct ieee802154_hw_addr_filt *filt, + unsigned long changed) +{ + struct bcfserial *bcfserial = hw->priv; + + if (changed & IEEE802154_AFILT_SADDR_CHANGED) { + u16 addr = le16_to_cpu(filt->short_addr); + dev_dbg(&bcfserial->serdev->dev, "Short Address changed %x\n", addr); + bcfserial_hdlc_send(bcfserial, SET_SHORT_ADDR, 0, 0, sizeof(addr), &addr); + } + + if (changed & IEEE802154_AFILT_PANID_CHANGED) { + u16 pan = le16_to_cpu(filt->pan_id); + dev_dbg(&bcfserial->serdev->dev, "PAN ID changed %x\n", pan); + bcfserial_hdlc_send(bcfserial, SET_PAN_ID, 0, 0, sizeof(pan), &pan); + } + + if (changed & IEEE802154_AFILT_IEEEADDR_CHANGED) { + u64 ieee_addr = le64_to_cpu(filt->ieee_addr); + dev_dbg(&bcfserial->serdev->dev, "IEEE Addr changed %llx\n", ieee_addr); + bcfserial_hdlc_send(bcfserial, SET_IEEE_ADDR, 0, 0, sizeof(ieee_addr), &ieee_addr); + } + return 0; +} + +static int bcfserial_set_txpower(struct ieee802154_hw *hw, s32 mbm) +{ + struct bcfserial *bcfserial = hw->priv; + dev_dbg(&bcfserial->serdev->dev, "SET TXPOWER\n"); + return -ENOTSUPP; +} + +static int bcfserial_set_lbt(struct ieee802154_hw *hw, bool on) +{ + struct bcfserial *bcfserial = hw->priv; + dev_dbg(&bcfserial->serdev->dev, "SET LBT\n"); + return -ENOTSUPP; +} + +static int bcfserial_set_cca_mode(struct ieee802154_hw *hw, + const struct wpan_phy_cca *cca) +{ + struct bcfserial *bcfserial = hw->priv; + dev_dbg(&bcfserial->serdev->dev, "SET CCA MODE\n"); + return -ENOTSUPP; +} + +static int bcfserial_set_cca_ed_level(struct ieee802154_hw *hw, s32 mbm) +{ + struct bcfserial *bcfserial = hw->priv; + dev_dbg(&bcfserial->serdev->dev, "SET CCA ED LEVEL\n"); + return -ENOTSUPP; +} + +static int bcfserial_set_csma_params(struct ieee802154_hw *hw, u8 min_be, u8 max_be, + u8 retries) +{ + struct bcfserial *bcfserial = hw->priv; + dev_dbg(&bcfserial->serdev->dev, "SET CSMA PARAMS\n"); + return -ENOTSUPP; +} + +static int bcfserial_set_frame_retries(struct ieee802154_hw *hw, s8 retries) +{ + struct bcfserial *bcfserial = hw->priv; + dev_dbg(&bcfserial->serdev->dev, "SET FRAME RETRIES\n"); + return -ENOTSUPP; +} + +static int bcfserial_set_promiscuous_mode(struct ieee802154_hw *hw, const bool on) +{ + struct bcfserial *bcfserial = hw->priv; + dev_dbg(&bcfserial->serdev->dev, "SET PROMISCUOUS\n"); + return -ENOTSUPP; +} + +static const struct ieee802154_ops bcfserial_ops = { + .owner = THIS_MODULE, + .start = bcfserial_start, + .stop = bcfserial_stop, + .xmit_async = bcfserial_xmit, + .ed = bcfserial_ed, + .set_channel = bcfserial_set_channel, + .set_hw_addr_filt = bcfserial_set_hw_addr_filt, + .set_txpower = bcfserial_set_txpower, + .set_lbt = bcfserial_set_lbt, + .set_cca_mode = bcfserial_set_cca_mode, + .set_cca_ed_level = bcfserial_set_cca_ed_level, + .set_csma_params = bcfserial_set_csma_params, + .set_frame_retries = bcfserial_set_frame_retries, + .set_promiscuous_mode = bcfserial_set_promiscuous_mode, +}; + +static void bcfserial_wpan_rx(struct bcfserial *bcfserial, const u8 *buffer, size_t count) +{ + struct sk_buff *skb; + u8 len, lqi; + + if (count == 1) { + // TX ACK + dev_dbg(&bcfserial->serdev->dev, "TX ACK: 0x%02x:0x%02x\n", buffer[0], bcfserial->tx_ack_seq); + + if (buffer[0] == bcfserial->tx_ack_seq && bcfserial->tx_skb) { + skb = bcfserial->tx_skb; + bcfserial->tx_skb = NULL; + ieee802154_xmit_complete(bcfserial->hw, skb, false); + } else { + dev_err(&bcfserial->serdev->dev, "unknown ack %u\n", bcfserial->tx_ack_seq); + } + } else if (bcfserial->response_size == count && bcfserial->response_buffer) { + //TODO replace with semaphore + dev_dbg(&bcfserial->serdev->dev, "Response size %u found\n", count); + memcpy(bcfserial->response_buffer, buffer, count); + bcfserial->response_size = 0; + } else { + // RX Packet + dev_dbg(&bcfserial->serdev->dev, "RX Packet Len:%u LQI:%u\n", buffer[0], buffer[count-1]); + len = buffer[0]; + lqi = buffer[count-1]; + + if (len+2 != count) { + dev_err(&bcfserial->serdev->dev, "RX Packet invalid length\n"); + return; + } + + if (!ieee802154_is_valid_psdu_len(len)) { + dev_err(&bcfserial->serdev->dev, "frame corrupted\n"); + return; + } + + skb = dev_alloc_skb(IEEE802154_MTU); + if (!skb) { + dev_err(&bcfserial->serdev->dev, "failed to allocate sk_buff\n"); + return; + } + + skb_put_data(skb, buffer+1, len); + ieee802154_rx_irqsafe(bcfserial->hw, skb, lqi); + } +} + +static int bcfserial_tty_receive(struct serdev_device *serdev, + const unsigned char *data, size_t count) +{ + struct bcfserial *bcfserial = serdev_device_get_drvdata(serdev); + u16 crc_check = 0; + size_t i; + u8 c; + + + for (i = 0; i < count; i++) { + c = data[i]; + + if (c == HDLC_FRAME) { + if (bcfserial->rx_address != 0xFF) { + crc_check = crc_ccitt(0xffff, &bcfserial->rx_address, 1); + crc_check = crc_ccitt(crc_check, bcfserial->rx_buffer, bcfserial->rx_offset); + + if (crc_check == 0xf0b8) { + if ((bcfserial->rx_buffer[0] & 1) == 0) { + //I-Frame, send S-Frame ACK + bcfserial_hdlc_send_ack(bcfserial, bcfserial->rx_address, (bcfserial->rx_buffer[0] >> 1) & 0x7); + } + + if (bcfserial->rx_address == ADDRESS_WPAN) { + bcfserial_wpan_rx(bcfserial, bcfserial->rx_buffer + 1, bcfserial->rx_offset - 3); + } + else if (bcfserial->rx_address == ADDRESS_CDC) { + bcfserial->rx_buffer[bcfserial->rx_offset-2] = 0; + dev_dbg(&bcfserial->serdev->dev, "> %s", bcfserial->rx_buffer+1); + } + } + else { + dev_err(&bcfserial->serdev->dev, "CRC Failed from %02x: 0x%04x\n", bcfserial->rx_address, crc_check); + } + } + bcfserial->rx_offset = 0; + bcfserial->rx_address = 0xFF; + } else if (c == HDLC_ESC) { + bcfserial->rx_in_esc = 1; + } else { + if (bcfserial->rx_in_esc) { + c ^= 0x20; + bcfserial->rx_in_esc = 0; + } + + if (bcfserial->rx_address == 0xFF) { + bcfserial->rx_address = c; + if (bcfserial->rx_address == ADDRESS_WPAN || + bcfserial->rx_address == ADDRESS_CDC || + bcfserial->rx_address == ADDRESS_HW) { + } else { + bcfserial->rx_address = 0xFF; + } + bcfserial->rx_offset = 0; + } else { + if (bcfserial->rx_offset < MAX_RX_HDLC) { + bcfserial->rx_buffer[bcfserial->rx_offset] = c; + bcfserial->rx_offset++; + } else { + //buffer overflow + dev_err(&bcfserial->serdev->dev, "RX Buffer Overflow\n"); + bcfserial->rx_address = 0xFF; + bcfserial->rx_offset = 0; + } + } + } + } + + return count; +} + +static void bcfserial_uart_transmit(struct work_struct *work) +{ + struct bcfserial *bcfserial = container_of(work, struct bcfserial, tx_work); + + spin_lock_bh(&bcfserial->tx_consumer_lock); + bcfserial_serdev_write_locked(bcfserial); + spin_unlock_bh(&bcfserial->tx_consumer_lock); +} + +static void bcfserial_tty_wakeup(struct serdev_device *serdev) +{ + struct bcfserial *bcfserial = serdev_device_get_drvdata(serdev); + + schedule_work(&bcfserial->tx_work); +} + +static struct serdev_device_ops bcfserial_serdev_ops = { + .receive_buf = bcfserial_tty_receive, + .write_wakeup = bcfserial_tty_wakeup, +}; + +static const struct of_device_id bcfserial_of_match[] = { + { + .compatible = "beagle,bcfserial", + }, + {} +}; +MODULE_DEVICE_TABLE(of, bcfserial_of_match); + +static const s32 channel_powers[] = { + 300, 280, 230, 180, 130, 70, 0, -100, -200, -300, -400, -500, -700, + -900, -1200, -1700, +}; + +static int bcfserial_get_device_capabilities(struct bcfserial *bcfserial) +{ + u32 valid_channels = 0; + int ret = 0; + struct ieee802154_hw *hw = bcfserial->hw; + + bcfserial_hdlc_send_cmd(bcfserial, RESET); + + ret = bcfserial_hdlc_receive(bcfserial, GET_SUPPORTED_CHANNELS, &valid_channels, sizeof(valid_channels)); + if (ret < 0) { + return ret; + } + dev_dbg(&bcfserial->serdev->dev, "Supported Channels %x\n", valid_channels); + + /* FIXME: these need to come from device capabilities */ + hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT; + + /* FIXME: these need to come from device capabilities */ + hw->phy->flags = WPAN_PHY_FLAG_TXPOWER; + + /* Set default and supported channels */ + hw->phy->current_page = 0; + hw->phy->current_channel = ffs(valid_channels) - 1; //set to lowest valid channel + hw->phy->supported.channels[0] = valid_channels; + + /* FIXME: these need to come from device capabilities */ + hw->phy->supported.tx_powers = channel_powers; + hw->phy->supported.tx_powers_size = ARRAY_SIZE(channel_powers); + hw->phy->transmit_power = hw->phy->supported.tx_powers[0]; + + return ret; +} + +static int bcfserial_probe(struct serdev_device *serdev) +{ + struct ieee802154_hw *hw; + struct bcfserial *bcfserial = NULL; + u32 speed = 115200; + int ret; + + hw = ieee802154_alloc_hw(sizeof(struct bcfserial), &bcfserial_ops); + if (!hw) + return -ENOMEM; + + bcfserial = hw->priv; + bcfserial->hw = hw; + hw->parent = &serdev->dev; + bcfserial->serdev = serdev; + + INIT_WORK(&bcfserial->tx_work, bcfserial_uart_transmit); + + spin_lock_init(&bcfserial->tx_producer_lock); + spin_lock_init(&bcfserial->tx_consumer_lock); + bcfserial->tx_circ_buf.head = 0; + bcfserial->tx_circ_buf.tail = 0; + bcfserial->tx_circ_buf.buf = devm_kmalloc(&serdev->dev, TX_CIRC_BUF_SIZE, GFP_KERNEL); + + bcfserial->rx_buffer = devm_kmalloc(&serdev->dev, MAX_RX_HDLC, GFP_KERNEL); + bcfserial->rx_offset = 0; + bcfserial->rx_address = 0xff; + bcfserial->rx_in_esc = 0; + + serdev_device_set_drvdata(serdev, bcfserial); + serdev_device_set_client_ops(serdev, &bcfserial_serdev_ops); + + ret = serdev_device_open(serdev); + if (ret) { + dev_err(&bcfserial->serdev->dev, "Unable to open device\n"); + goto fail_hw; + } + + speed = serdev_device_set_baudrate(serdev, speed); + dev_dbg(&bcfserial->serdev->dev, "Using baudrate %u\n", speed); + + serdev_device_set_flow_control(serdev, false); + + bcfserial_hdlc_send_ack(bcfserial, 0x41, 0x00); + + ret = bcfserial_get_device_capabilities(bcfserial); + + if (ret < 0) { + // dev_err(&udev->dev, "Failed to get device capabilities"); + dev_err(&bcfserial->serdev->dev, "Failed to get device capabilities\n"); + goto fail; + } + + ret = ieee802154_register_hw(hw); + + dev_info(&bcfserial->serdev->dev, "bcfserial started"); + if (ret) + goto fail; + + return 0; + +fail: + dev_err(&bcfserial->serdev->dev, "Closing serial device on failure\n"); + serdev_device_close(serdev); +fail_hw: + dev_err(&bcfserial->serdev->dev, "Failed to open bcfserial\n"); + ieee802154_free_hw(hw); + return ret; +} + +static void bcfserial_remove(struct serdev_device *serdev) +{ + struct bcfserial *bcfserial = serdev_device_get_drvdata(serdev); + dev_info(&bcfserial->serdev->dev, "Closing serial device\n"); + ieee802154_unregister_hw(bcfserial->hw); + flush_work(&bcfserial->tx_work); + ieee802154_free_hw(bcfserial->hw); + serdev_device_close(serdev); +} + +static struct serdev_device_driver bcfserial_driver = { + .probe = bcfserial_probe, + .remove = bcfserial_remove, + .driver = { + .name = BCFSERIAL_DRV_NAME, + .of_match_table = of_match_ptr(bcfserial_of_match), + }, +}; + +module_serdev_device_driver(bcfserial_driver); + +MODULE_DESCRIPTION("WPAN serial driver for BeagleConnect"); +MODULE_AUTHOR("Erik Larson "); +MODULE_VERSION("0.1.1"); +MODULE_LICENSE("GPL v2"); From bf699ac0076aad41d794c216f9453e92dc57e3bb Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Thu, 11 Nov 2021 13:52:45 -0600 Subject: [PATCH 10/65] Add BCFSERIAL driver Signed-off-by: Robert Nelson --- drivers/net/ieee802154/Kconfig | 9 +++++++++ drivers/net/ieee802154/Makefile | 1 + 2 files changed, 10 insertions(+) diff --git a/drivers/net/ieee802154/Kconfig b/drivers/net/ieee802154/Kconfig index 0a1d87691d50c..77b48e196eae3 100644 --- a/drivers/net/ieee802154/Kconfig +++ b/drivers/net/ieee802154/Kconfig @@ -136,3 +136,12 @@ config IEEE802154_HWSIM This driver can also be built as a module. To do so say M here. The module will be called 'mac802154_hwsim'. + +config IEEE802154_BCFSERIAL + tristate "BCFSERIAL driver" + depends on IEEE802154_DRIVERS && MAC802154 + help + Adds support for BCFSERIAL 802.15.4 adapters. + + This driver should work with at least the following devices: + * BeagleBoard.org BeagleConnect Freedom diff --git a/drivers/net/ieee802154/Makefile b/drivers/net/ieee802154/Makefile index 59442aab94038..1b3ea397b91a9 100644 --- a/drivers/net/ieee802154/Makefile +++ b/drivers/net/ieee802154/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_IEEE802154_ADF7242) += adf7242.o obj-$(CONFIG_IEEE802154_CA8210) += ca8210.o obj-$(CONFIG_IEEE802154_MCR20A) += mcr20a.o obj-$(CONFIG_IEEE802154_HWSIM) += mac802154_hwsim.o +obj-$(CONFIG_IEEE802154_BCFSERIAL) += bcfserial.o From 8edac18eef24669c5f13f853ae10aa0e7a029292 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Fri, 3 Feb 2023 19:23:24 -0600 Subject: [PATCH 11/65] merge: ksmbd: https://github.com/cifsd-team/ksmbd https://github.com/cifsd-team/ksmbd/commit/9912f7960ee50fdb42daa28d198dc214fb92983f Signed-off-by: Robert Nelson --- fs/ksmbd/Kconfig | 79 + fs/ksmbd/Makefile | 75 + fs/ksmbd/README.md | 170 + fs/ksmbd/asn1.c | 342 + fs/ksmbd/asn1.h | 21 + fs/ksmbd/auth.c | 1541 ++++ fs/ksmbd/auth.h | 85 + fs/ksmbd/build_ksmbd.sh | 177 + fs/ksmbd/connection.c | 470 ++ fs/ksmbd/connection.h | 210 + fs/ksmbd/crypto_ctx.c | 282 + fs/ksmbd/crypto_ctx.h | 74 + fs/ksmbd/dkms.conf | 6 + fs/ksmbd/glob.h | 58 + fs/ksmbd/ksmbd.rst | 183 + fs/ksmbd/ksmbd_netlink.h | 413 ++ fs/ksmbd/ksmbd_spnego_negtokeninit.asn1 | 31 + fs/ksmbd/ksmbd_spnego_negtokentarg.asn1 | 19 + fs/ksmbd/ksmbd_work.c | 79 + fs/ksmbd/ksmbd_work.h | 117 + fs/ksmbd/mgmt/ksmbd_ida.c | 57 + fs/ksmbd/mgmt/ksmbd_ida.h | 40 + fs/ksmbd/mgmt/share_config.c | 234 + fs/ksmbd/mgmt/share_config.h | 82 + fs/ksmbd/mgmt/tree_connect.c | 136 + fs/ksmbd/mgmt/tree_connect.h | 58 + fs/ksmbd/mgmt/user_config.c | 79 + fs/ksmbd/mgmt/user_config.h | 68 + fs/ksmbd/mgmt/user_session.c | 402 ++ fs/ksmbd/mgmt/user_session.h | 108 + fs/ksmbd/misc.c | 488 ++ fs/ksmbd/misc.h | 37 + fs/ksmbd/ndr.c | 534 ++ fs/ksmbd/ndr.h | 22 + fs/ksmbd/netmisc.c | 606 ++ fs/ksmbd/nterr.h | 543 ++ fs/ksmbd/ntlmssp.h | 169 + fs/ksmbd/oplock.c | 1994 +++++ fs/ksmbd/oplock.h | 138 + fs/ksmbd/server.c | 630 ++ fs/ksmbd/server.h | 71 + fs/ksmbd/smb1misc.c | 297 + fs/ksmbd/smb1ops.c | 93 + fs/ksmbd/smb1pdu.c | 8511 ++++++++++++++++++++++ fs/ksmbd/smb1pdu.h | 1600 +++++ fs/ksmbd/smb2misc.c | 450 ++ fs/ksmbd/smb2ops.c | 367 + fs/ksmbd/smb2pdu.c | 8799 +++++++++++++++++++++++ fs/ksmbd/smb2pdu.h | 1712 +++++ fs/ksmbd/smb_common.c | 746 ++ fs/ksmbd/smb_common.h | 534 ++ fs/ksmbd/smbacl.c | 1505 ++++ fs/ksmbd/smbacl.h | 283 + fs/ksmbd/smberr.h | 235 + fs/ksmbd/smbfsctl.h | 91 + fs/ksmbd/smbstatus.h | 1822 +++++ fs/ksmbd/transport_ipc.c | 885 +++ fs/ksmbd/transport_ipc.h | 47 + fs/ksmbd/transport_rdma.c | 2297 ++++++ fs/ksmbd/transport_rdma.h | 69 + fs/ksmbd/transport_tcp.c | 678 ++ fs/ksmbd/transport_tcp.h | 13 + fs/ksmbd/unicode.c | 402 ++ fs/ksmbd/unicode.h | 361 + fs/ksmbd/uniupr.h | 268 + fs/ksmbd/vfs.c | 2531 +++++++ fs/ksmbd/vfs.h | 243 + fs/ksmbd/vfs_cache.c | 763 ++ fs/ksmbd/vfs_cache.h | 184 + fs/ksmbd/xattr.h | 122 + 70 files changed, 46836 insertions(+) create mode 100644 fs/ksmbd/Kconfig create mode 100644 fs/ksmbd/Makefile create mode 100644 fs/ksmbd/README.md create mode 100644 fs/ksmbd/asn1.c create mode 100644 fs/ksmbd/asn1.h create mode 100644 fs/ksmbd/auth.c create mode 100644 fs/ksmbd/auth.h create mode 100755 fs/ksmbd/build_ksmbd.sh create mode 100644 fs/ksmbd/connection.c create mode 100644 fs/ksmbd/connection.h create mode 100644 fs/ksmbd/crypto_ctx.c create mode 100644 fs/ksmbd/crypto_ctx.h create mode 100644 fs/ksmbd/dkms.conf create mode 100644 fs/ksmbd/glob.h create mode 100644 fs/ksmbd/ksmbd.rst create mode 100644 fs/ksmbd/ksmbd_netlink.h create mode 100644 fs/ksmbd/ksmbd_spnego_negtokeninit.asn1 create mode 100644 fs/ksmbd/ksmbd_spnego_negtokentarg.asn1 create mode 100644 fs/ksmbd/ksmbd_work.c create mode 100644 fs/ksmbd/ksmbd_work.h create mode 100644 fs/ksmbd/mgmt/ksmbd_ida.c create mode 100644 fs/ksmbd/mgmt/ksmbd_ida.h create mode 100644 fs/ksmbd/mgmt/share_config.c create mode 100644 fs/ksmbd/mgmt/share_config.h create mode 100644 fs/ksmbd/mgmt/tree_connect.c create mode 100644 fs/ksmbd/mgmt/tree_connect.h create mode 100644 fs/ksmbd/mgmt/user_config.c create mode 100644 fs/ksmbd/mgmt/user_config.h create mode 100644 fs/ksmbd/mgmt/user_session.c create mode 100644 fs/ksmbd/mgmt/user_session.h create mode 100644 fs/ksmbd/misc.c create mode 100644 fs/ksmbd/misc.h create mode 100644 fs/ksmbd/ndr.c create mode 100644 fs/ksmbd/ndr.h create mode 100644 fs/ksmbd/netmisc.c create mode 100644 fs/ksmbd/nterr.h create mode 100644 fs/ksmbd/ntlmssp.h create mode 100644 fs/ksmbd/oplock.c create mode 100644 fs/ksmbd/oplock.h create mode 100644 fs/ksmbd/server.c create mode 100644 fs/ksmbd/server.h create mode 100644 fs/ksmbd/smb1misc.c create mode 100644 fs/ksmbd/smb1ops.c create mode 100644 fs/ksmbd/smb1pdu.c create mode 100644 fs/ksmbd/smb1pdu.h create mode 100644 fs/ksmbd/smb2misc.c create mode 100644 fs/ksmbd/smb2ops.c create mode 100644 fs/ksmbd/smb2pdu.c create mode 100644 fs/ksmbd/smb2pdu.h create mode 100644 fs/ksmbd/smb_common.c create mode 100644 fs/ksmbd/smb_common.h create mode 100644 fs/ksmbd/smbacl.c create mode 100644 fs/ksmbd/smbacl.h create mode 100644 fs/ksmbd/smberr.h create mode 100644 fs/ksmbd/smbfsctl.h create mode 100644 fs/ksmbd/smbstatus.h create mode 100644 fs/ksmbd/transport_ipc.c create mode 100644 fs/ksmbd/transport_ipc.h create mode 100644 fs/ksmbd/transport_rdma.c create mode 100644 fs/ksmbd/transport_rdma.h create mode 100644 fs/ksmbd/transport_tcp.c create mode 100644 fs/ksmbd/transport_tcp.h create mode 100644 fs/ksmbd/unicode.c create mode 100644 fs/ksmbd/unicode.h create mode 100644 fs/ksmbd/uniupr.h create mode 100644 fs/ksmbd/vfs.c create mode 100644 fs/ksmbd/vfs.h create mode 100644 fs/ksmbd/vfs_cache.c create mode 100644 fs/ksmbd/vfs_cache.h create mode 100644 fs/ksmbd/xattr.h diff --git a/fs/ksmbd/Kconfig b/fs/ksmbd/Kconfig new file mode 100644 index 0000000000000..feee79529cbd7 --- /dev/null +++ b/fs/ksmbd/Kconfig @@ -0,0 +1,79 @@ +config SMB_SERVER + tristate "SMB3 server support (EXPERIMENTAL)" + depends on INET + depends on MULTIUSER + depends on FILE_LOCKING + select NLS + select NLS_UTF8 + select CRYPTO + select CRYPTO_MD4 + select CRYPTO_MD5 + select CRYPTO_HMAC + select CRYPTO_ECB + select CRYPTO_LIB_DES + select CRYPTO_SHA256 + select CRYPTO_CMAC + select CRYPTO_SHA512 + select CRYPTO_AEAD2 + select CRYPTO_CCM + select CRYPTO_GCM + select ASN1 + select OID_REGISTRY + select CRC32 + default n + help + Choose Y here if you want to allow SMB3 compliant clients + to access files residing on this system using SMB3 protocol. + To compile the SMB3 server support as a module, + choose M here: the module will be called ksmbd. + + You may choose to use a samba server instead, in which + case you can choose N here. + + You also need to install user space programs which can be found + in ksmbd-tools, available from + https://github.com/cifsd-team/ksmbd-tools. + More detail about how to run the ksmbd kernel server is + available via README file + (https://github.com/cifsd-team/ksmbd-tools/blob/master/README). + + ksmbd kernel server includes support for auto-negotiation, + Secure negotiate, Pre-authentication integrity, oplock/lease, + compound requests, multi-credit, packet signing, RDMA(smbdirect), + smb3 encryption, copy-offload, secure per-user session + establishment via NTLM or NTLMv2. + +config SMB_INSECURE_SERVER + bool "Support for insecure SMB1/CIFS and SMB2.0 protocols" + depends on SMB_SERVER && INET + select NLS + default n + + help + This enables deprecated insecure protocols dialects: SMB1/CIFS + and SMB2.0 + +config SMB_SERVER_SMBDIRECT + bool "Support for SMB Direct protocol" + depends on SMB_SERVER=m && INFINIBAND && INFINIBAND_ADDR_TRANS || SMB_SERVER=y && INFINIBAND=y && INFINIBAND_ADDR_TRANS=y + select SG_POOL + default n + + help + Enables SMB Direct support for SMB 3.0, 3.02 and 3.1.1. + + SMB Direct allows transferring SMB packets over RDMA. If unsure, + say N. + +config SMB_SERVER_CHECK_CAP_NET_ADMIN + bool "Enable check network administration capability" + depends on SMB_SERVER + default y + + help + Prevent unprivileged processes to start the ksmbd kernel server. + +config SMB_SERVER_KERBEROS5 + bool "Support for Kerberos 5" + depends on SMB_SERVER + default n diff --git a/fs/ksmbd/Makefile b/fs/ksmbd/Makefile new file mode 100644 index 0000000000000..7f332c397afc6 --- /dev/null +++ b/fs/ksmbd/Makefile @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Makefile for Linux SMB3 kernel server +# +ifneq ($(KERNELRELEASE),) +# For kernel build + +# CONFIG_SMB_SERVER_SMBDIRECT is supported in the kernel above 4.12 version. +SMBDIRECT_SUPPORTED = $(shell [ $(VERSION) -gt 4 -o \( $(VERSION) -eq 4 -a \ + $(PATCHLEVEL) -gt 12 \) ] && echo y) + +ifeq "$(CONFIG_SMB_SERVER_SMBDIRECT)" "y" +ifneq "$(call SMBDIRECT_SUPPORTED)" "y" +$(error CONFIG_SMB_SERVER_SMBDIRECT is supported in the kernel above 4.12 version) +endif +endif + +obj-$(CONFIG_SMB_SERVER) += ksmbd.o + +ksmbd-y := unicode.o auth.o vfs.o vfs_cache.o connection.o crypto_ctx.o \ + server.o misc.o oplock.o ksmbd_work.o smbacl.o ndr.o\ + mgmt/ksmbd_ida.o mgmt/user_config.o mgmt/share_config.o \ + mgmt/tree_connect.o mgmt/user_session.o smb_common.o \ + transport_tcp.o transport_ipc.o + +ksmbd-y += smb2pdu.o smb2ops.o smb2misc.o ksmbd_spnego_negtokeninit.asn1.o \ + ksmbd_spnego_negtokentarg.asn1.o asn1.o + +$(obj)/asn1.o: $(obj)/ksmbd_spnego_negtokeninit.asn1.h $(obj)/ksmbd_spnego_negtokentarg.asn1.h + +$(obj)/ksmbd_spnego_negtokeninit.asn1.o: $(obj)/ksmbd_spnego_negtokeninit.asn1.c $(obj)/ksmbd_spnego_negtokeninit.asn1.h +$(obj)/ksmbd_spnego_negtokentarg.asn1.o: $(obj)/ksmbd_spnego_negtokentarg.asn1.c $(obj)/ksmbd_spnego_negtokentarg.asn1.h + +ksmbd-$(CONFIG_SMB_INSECURE_SERVER) += smb1pdu.o smb1ops.o smb1misc.o netmisc.o +ksmbd-$(CONFIG_SMB_SERVER_SMBDIRECT) += transport_rdma.o +else +# For external module build +EXTRA_FLAGS += -I$(PWD) +KDIR ?= /lib/modules/$(shell uname -r)/build +MDIR ?= /lib/modules/$(shell uname -r) +PWD := $(shell pwd) +PWD := $(shell pwd) + +export CONFIG_SMB_SERVER := m + +all: + $(MAKE) -C $(KDIR) M=$(PWD) modules + +clean: + $(MAKE) -C $(KDIR) M=$(PWD) clean + +install: ksmbd.ko + rm -f ${MDIR}/kernel/fs/ksmbd/ksmbd.ko + install -m644 -b -D ksmbd.ko ${MDIR}/kernel/fs/ksmbd/ksmbd.ko + depmod -a + +# install dkms +PKGVER=$(shell echo `git rev-parse --short HEAD`) +dkms-install: + sudo rm -rf "/usr/src/ksmbd*" + sudo cp -r "$(PWD)" "/usr/src/ksmbd-$(PKGVER)" + sudo sed -e "s/@VERSION@/$(PKGVER)/" -i "/usr/src/ksmbd-$(PKGVER)/dkms.conf" + sudo dkms install -m ksmbd/$(PKGVER) --force + +dkms-uninstall: + sudo modprobe -r ksmbd + sudo dkms remove ksmbd/$(PKGVER) + sudo rm -rf "/usr/src/ksmbd-$(PKGVER)" + +uninstall: + rm -rf ${MDIR}/kernel/fs/ksmbd + depmod -a +endif + +.PHONY : all clean install uninstall diff --git a/fs/ksmbd/README.md b/fs/ksmbd/README.md new file mode 100644 index 0000000000000..2726cefe4e476 --- /dev/null +++ b/fs/ksmbd/README.md @@ -0,0 +1,170 @@ + +# Content + +- [What is KSMBD?](#KSMBDwhat-is-ksmbd) +- [Under PFIF](#under-pfif) +- [Git](#git) +- [Maintainers](#maintainers) +- [Bug reports or contribution](#Bug-reports-or-contribution) +- [Features](#features) +- [Supported Linux Kernel Versions](#supported-linux-kernel-versions) +- [KSMBD architecture](#ksmbd-architecture) + + +## What is KSMBD? + +KSMBD is an opensource In-kernel CIFS/SMB3 server created by Namjae Jeon for Linux Kernel. It's an implementation of SMB/CIFS protocol in kernel space for sharing files and IPC services over network. Initially the target is to provide improved file I/O performances, but the bigger goal is to have some new features which are much easier to develop and maintain inside the kernel and expose the layers fully. Directions can be attributed to sections where SAMBA is moving to few modules inside the kernel to have features like RDMA(Remote direct memory access) to work with actual performance gain. + + +## Under PFIF + +This code was developed in participation with the Protocol Freedom Information Foundation. + +Please see +* http://samba.org/samba/PFIF/ +for more details. + + +## Git + +The development git tree is available at +* https://github.com/cifsd-team/ksmbd +* https://github.com/cifsd-team/ksmbd-tools + + +## Maintainers + +* Namjae Jeon + + +## Bug reports or contribution + +For reporting bugs and sending patches, please send the patches to the following mail address: + +* linkinjeon@kernel.org + +or open issues/send PRs to [KSMBD](https://github.com/cifsd-team/ksmbd). + +## Installing as a stand-alone module + +Install prerequisite package for Fedora, RHEL: +``` + yum install kernel-devel-$(uname -r) +``` + +Build step: +``` + make + sudo make install +``` + +To load the driver manually, run this as root: +``` + modprobe ksmbd +``` + + +## Installing as a part of the kernel + +1. Let's take [linux] as the path to your kernel source dir. +``` + cd [linux] + cp -ar ksmbd [linux]/fs/ +``` + +2. edit [linux]/fs/Kconfig +``` + source "fs/cifs/Kconfig" + +source "fs/ksmbd/Kconfig" + source "fs/coda/Kconfig" +``` + +3. edit [linux]/fs/Makefile +``` + obj-$(CONFIG_CIFS) += cifs/ + +obj-$(CONFIG_SMB_SERVER) += ksmbd/ + obj-$(CONFIG_HPFS_FS) += hpfs/ +``` +4. make menuconfig and set ksmbd +``` + [*] Network File Systems ---> + SMB server support +``` + +build your kernel + + +## Features + +*Implemented* +1. SMB1(CIFS), SMB2/3 protocols for basic file sharing +2. Dynamic crediting +3. Compound requests +4. oplock/lease +5. Large MTU +6. NTLM/NTLMv2 +7. Auto negotiation +8. HMAC-SHA256 Signing +9. Secure negotiate +10. Signing Update +11. Pre-authentication integrity(SMB 3.1.1) +12. SMB3 encryption(CCM, GCM) +13. SMB direct(RDMA) +14. Win-ACL +15. Kerberos +16. Multi-channel + +*Planned* +1. Durable handle v1/v2 +2. Persistent handles +3. Directory lease + + +## Supported Linux Kernel Versions + +* Linux Kernel 5.4 or later + + +## KSMBD architecture + +``` + |--- ... + --------|--- ksmbd/3 - Client 3 + |-------|--- ksmbd/2 - Client 2 + | | _____________________________________________________ + | | |- Client 1 | +<--- Socket ---|--- ksmbd/1 <<= Authentication : NTLM/NTLM2, Kerberos(TODO)| + | | | | <<= SMB : SMB1, SMB2, SMB2.1, SMB3, SMB3.0.2, | + | | | | SMB3.1.1 | + | | | |_____________________________________________________| + | | | + | | |--- VFS --- Local Filesystem + | | +KERNEL |--- ksmbd/0(forker kthread) +---------------||--------------------------------------------------------------- +USER || + || communication using NETLINK + || ______________________________________________ + || | | + ksmbd.mountd <<= DCE/RPC, WINREG | + ^ | <<= configure shares setting, user accounts | + | |______________________________________________| + | + |------ smb.conf(config file) + | + |------ ksmbdpwd.db(user account/password file) + ^ + ksmbd.adduser ---------------| + +``` + +## Performance + +1. ksmbd vs samba performance comparison using iozone (Linux Client) +

+ +2. ksmbd vs samba performance comparison using fileop (Linux Client) +

+ +3. ksmbd vs samba performance comparison using CrystalDiskMark (Windows Client) +

![CrystalDiskMark](https://github.com/cifsd-team/cifsd-perf/blob/master/CrystalDiskMark_Performance.JPG) diff --git a/fs/ksmbd/asn1.c b/fs/ksmbd/asn1.c new file mode 100644 index 0000000000000..2276624e47ef4 --- /dev/null +++ b/fs/ksmbd/asn1.c @@ -0,0 +1,342 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * The ASB.1/BER parsing code is derived from ip_nat_snmp_basic.c which was in + * turn derived from the gxsnmp package by Gregory McLean & Jochen Friedrich + * + * Copyright (c) 2000 RP Internet (www.rpi.net.au). + */ + +#include +#include +#include +#include +#include +#include + +#include "glob.h" + +#include "asn1.h" +#include "connection.h" +#include "auth.h" +#include "ksmbd_spnego_negtokeninit.asn1.h" +#include "ksmbd_spnego_negtokentarg.asn1.h" + +#define SPNEGO_OID_LEN 7 +#define NTLMSSP_OID_LEN 10 +#define KRB5_OID_LEN 7 +#define KRB5U2U_OID_LEN 8 +#define MSKRB5_OID_LEN 7 +static unsigned long SPNEGO_OID[7] = { 1, 3, 6, 1, 5, 5, 2 }; +static unsigned long NTLMSSP_OID[10] = { 1, 3, 6, 1, 4, 1, 311, 2, 2, 10 }; +static unsigned long KRB5_OID[7] = { 1, 2, 840, 113554, 1, 2, 2 }; +static unsigned long KRB5U2U_OID[8] = { 1, 2, 840, 113554, 1, 2, 2, 3 }; +static unsigned long MSKRB5_OID[7] = { 1, 2, 840, 48018, 1, 2, 2 }; + +static char NTLMSSP_OID_STR[NTLMSSP_OID_LEN] = { 0x2b, 0x06, 0x01, 0x04, 0x01, + 0x82, 0x37, 0x02, 0x02, 0x0a }; + +static bool +asn1_subid_decode(const unsigned char **begin, const unsigned char *end, + unsigned long *subid) +{ + const unsigned char *ptr = *begin; + unsigned char ch; + + *subid = 0; + + do { + if (ptr >= end) + return false; + + ch = *ptr++; + *subid <<= 7; + *subid |= ch & 0x7F; + } while ((ch & 0x80) == 0x80); + + *begin = ptr; + return true; +} + +static bool asn1_oid_decode(const unsigned char *value, size_t vlen, + unsigned long **oid, size_t *oidlen) +{ + const unsigned char *iptr = value, *end = value + vlen; + unsigned long *optr; + unsigned long subid; + + vlen += 1; + if (vlen < 2 || vlen > UINT_MAX / sizeof(unsigned long)) + goto fail_nullify; + + *oid = kmalloc(vlen * sizeof(unsigned long), GFP_KERNEL); + if (!*oid) + return false; + + optr = *oid; + + if (!asn1_subid_decode(&iptr, end, &subid)) + goto fail; + + if (subid < 40) { + optr[0] = 0; + optr[1] = subid; + } else if (subid < 80) { + optr[0] = 1; + optr[1] = subid - 40; + } else { + optr[0] = 2; + optr[1] = subid - 80; + } + + *oidlen = 2; + optr += 2; + + while (iptr < end) { + if (++(*oidlen) > vlen) + goto fail; + + if (!asn1_subid_decode(&iptr, end, optr++)) + goto fail; + } + return true; + +fail: + kfree(*oid); +fail_nullify: + *oid = NULL; + return false; +} + +static bool oid_eq(unsigned long *oid1, unsigned int oid1len, + unsigned long *oid2, unsigned int oid2len) +{ + if (oid1len != oid2len) + return false; + + return memcmp(oid1, oid2, oid1len) == 0; +} + +int +ksmbd_decode_negTokenInit(unsigned char *security_blob, int length, + struct ksmbd_conn *conn) +{ + return asn1_ber_decoder(&ksmbd_spnego_negtokeninit_decoder, conn, + security_blob, length); +} + +int +ksmbd_decode_negTokenTarg(unsigned char *security_blob, int length, + struct ksmbd_conn *conn) +{ + return asn1_ber_decoder(&ksmbd_spnego_negtokentarg_decoder, conn, + security_blob, length); +} + +static int compute_asn_hdr_len_bytes(int len) +{ + if (len > 0xFFFFFF) + return 4; + else if (len > 0xFFFF) + return 3; + else if (len > 0xFF) + return 2; + else if (len > 0x7F) + return 1; + else + return 0; +} + +static void encode_asn_tag(char *buf, unsigned int *ofs, char tag, char seq, + int length) +{ + int i; + int index = *ofs; + char hdr_len = compute_asn_hdr_len_bytes(length); + int len = length + 2 + hdr_len; + + /* insert tag */ + buf[index++] = tag; + + if (!hdr_len) { + buf[index++] = len; + } else { + buf[index++] = 0x80 | hdr_len; + for (i = hdr_len - 1; i >= 0; i--) + buf[index++] = (len >> (i * 8)) & 0xFF; + } + + /* insert seq */ + len = len - (index - *ofs); + buf[index++] = seq; + + if (!hdr_len) { + buf[index++] = len; + } else { + buf[index++] = 0x80 | hdr_len; + for (i = hdr_len - 1; i >= 0; i--) + buf[index++] = (len >> (i * 8)) & 0xFF; + } + + *ofs += (index - *ofs); +} + +int build_spnego_ntlmssp_neg_blob(unsigned char **pbuffer, u16 *buflen, + char *ntlm_blob, int ntlm_blob_len) +{ + char *buf; + unsigned int ofs = 0; + int neg_result_len = 4 + compute_asn_hdr_len_bytes(1) * 2 + 1; + int oid_len = 4 + compute_asn_hdr_len_bytes(NTLMSSP_OID_LEN) * 2 + + NTLMSSP_OID_LEN; + int ntlmssp_len = 4 + compute_asn_hdr_len_bytes(ntlm_blob_len) * 2 + + ntlm_blob_len; + int total_len = 4 + compute_asn_hdr_len_bytes(neg_result_len + + oid_len + ntlmssp_len) * 2 + + neg_result_len + oid_len + ntlmssp_len; + + buf = kmalloc(total_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* insert main gss header */ + encode_asn_tag(buf, &ofs, 0xa1, 0x30, neg_result_len + oid_len + + ntlmssp_len); + + /* insert neg result */ + encode_asn_tag(buf, &ofs, 0xa0, 0x0a, 1); + buf[ofs++] = 1; + + /* insert oid */ + encode_asn_tag(buf, &ofs, 0xa1, 0x06, NTLMSSP_OID_LEN); + memcpy(buf + ofs, NTLMSSP_OID_STR, NTLMSSP_OID_LEN); + ofs += NTLMSSP_OID_LEN; + + /* insert response token - ntlmssp blob */ + encode_asn_tag(buf, &ofs, 0xa2, 0x04, ntlm_blob_len); + memcpy(buf + ofs, ntlm_blob, ntlm_blob_len); + ofs += ntlm_blob_len; + + *pbuffer = buf; + *buflen = total_len; + return 0; +} + +int build_spnego_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, + int neg_result) +{ + char *buf; + unsigned int ofs = 0; + int neg_result_len = 4 + compute_asn_hdr_len_bytes(1) * 2 + 1; + int total_len = 4 + compute_asn_hdr_len_bytes(neg_result_len) * 2 + + neg_result_len; + + buf = kmalloc(total_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* insert main gss header */ + encode_asn_tag(buf, &ofs, 0xa1, 0x30, neg_result_len); + + /* insert neg result */ + encode_asn_tag(buf, &ofs, 0xa0, 0x0a, 1); + if (neg_result) + buf[ofs++] = 2; + else + buf[ofs++] = 0; + + *pbuffer = buf; + *buflen = total_len; + return 0; +} + +int ksmbd_gssapi_this_mech(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + unsigned long *oid; + size_t oidlen; + int err = 0; + + if (!asn1_oid_decode(value, vlen, &oid, &oidlen)) { + err = -EBADMSG; + goto out; + } + + if (!oid_eq(oid, oidlen, SPNEGO_OID, SPNEGO_OID_LEN)) + err = -EBADMSG; + kfree(oid); +out: + if (err) { + char buf[50]; + + sprint_oid(value, vlen, buf, sizeof(buf)); + ksmbd_debug(AUTH, "Unexpected OID: %s\n", buf); + } + return err; +} + +int ksmbd_neg_token_init_mech_type(void *context, size_t hdrlen, + unsigned char tag, const void *value, + size_t vlen) +{ + struct ksmbd_conn *conn = context; + unsigned long *oid; + size_t oidlen; + int mech_type; + char buf[50]; + + if (!asn1_oid_decode(value, vlen, &oid, &oidlen)) + goto fail; + + if (oid_eq(oid, oidlen, NTLMSSP_OID, NTLMSSP_OID_LEN)) + mech_type = KSMBD_AUTH_NTLMSSP; + else if (oid_eq(oid, oidlen, MSKRB5_OID, MSKRB5_OID_LEN)) + mech_type = KSMBD_AUTH_MSKRB5; + else if (oid_eq(oid, oidlen, KRB5_OID, KRB5_OID_LEN)) + mech_type = KSMBD_AUTH_KRB5; + else if (oid_eq(oid, oidlen, KRB5U2U_OID, KRB5U2U_OID_LEN)) + mech_type = KSMBD_AUTH_KRB5U2U; + else + goto fail; + + conn->auth_mechs |= mech_type; + if (conn->preferred_auth_mech == 0) + conn->preferred_auth_mech = mech_type; + + kfree(oid); + return 0; + +fail: + kfree(oid); + sprint_oid(value, vlen, buf, sizeof(buf)); + ksmbd_debug(AUTH, "Unexpected OID: %s\n", buf); + return -EBADMSG; +} + +static int ksmbd_neg_token_alloc(void *context, size_t hdrlen, + unsigned char tag, const void *value, + size_t vlen) +{ + struct ksmbd_conn *conn = context; + + conn->mechToken = kmalloc(vlen + 1, GFP_KERNEL); + if (!conn->mechToken) + return -ENOMEM; + + memcpy(conn->mechToken, value, vlen); + conn->mechToken[vlen] = '\0'; + return 0; +} + +int ksmbd_neg_token_init_mech_token(void *context, size_t hdrlen, + unsigned char tag, const void *value, + size_t vlen) +{ + return ksmbd_neg_token_alloc(context, hdrlen, tag, value, vlen); +} + +int ksmbd_neg_token_targ_resp_token(void *context, size_t hdrlen, + unsigned char tag, const void *value, + size_t vlen) +{ + return ksmbd_neg_token_alloc(context, hdrlen, tag, value, vlen); +} diff --git a/fs/ksmbd/asn1.h b/fs/ksmbd/asn1.h new file mode 100644 index 0000000000000..ce105f4ce305a --- /dev/null +++ b/fs/ksmbd/asn1.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * The ASB.1/BER parsing code is derived from ip_nat_snmp_basic.c which was in + * turn derived from the gxsnmp package by Gregory McLean & Jochen Friedrich + * + * Copyright (c) 2000 RP Internet (www.rpi.net.au). + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __ASN1_H__ +#define __ASN1_H__ + +int ksmbd_decode_negTokenInit(unsigned char *security_blob, int length, + struct ksmbd_conn *conn); +int ksmbd_decode_negTokenTarg(unsigned char *security_blob, int length, + struct ksmbd_conn *conn); +int build_spnego_ntlmssp_neg_blob(unsigned char **pbuffer, u16 *buflen, + char *ntlm_blob, int ntlm_blob_len); +int build_spnego_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, + int neg_result); +#endif /* __ASN1_H__ */ diff --git a/fs/ksmbd/auth.c b/fs/ksmbd/auth.c new file mode 100644 index 0000000000000..ef620452f83ae --- /dev/null +++ b/fs/ksmbd/auth.c @@ -0,0 +1,1541 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "auth.h" +#include "glob.h" + +#include +#include + +#include "server.h" +#include "smb_common.h" +#include "connection.h" +#include "mgmt/user_session.h" +#include "mgmt/user_config.h" +#include "crypto_ctx.h" +#include "transport_ipc.h" + +/* + * Fixed format data defining GSS header and fixed string + * "not_defined_in_RFC4178@please_ignore". + * So sec blob data in neg phase could be generated statically. + */ +static char NEGOTIATE_GSS_HEADER[AUTH_GSS_LENGTH] = { +#ifdef CONFIG_SMB_SERVER_KERBEROS5 + 0x60, 0x5e, 0x06, 0x06, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x02, 0xa0, 0x54, 0x30, 0x52, 0xa0, 0x24, + 0x30, 0x22, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x12, 0x01, 0x02, 0x02, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x82, 0xf7, 0x12, 0x01, 0x02, 0x02, + 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, + 0x37, 0x02, 0x02, 0x0a, 0xa3, 0x2a, 0x30, 0x28, + 0xa0, 0x26, 0x1b, 0x24, 0x6e, 0x6f, 0x74, 0x5f, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x5f, + 0x69, 0x6e, 0x5f, 0x52, 0x46, 0x43, 0x34, 0x31, + 0x37, 0x38, 0x40, 0x70, 0x6c, 0x65, 0x61, 0x73, + 0x65, 0x5f, 0x69, 0x67, 0x6e, 0x6f, 0x72, 0x65 +#else + 0x60, 0x48, 0x06, 0x06, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x02, 0xa0, 0x3e, 0x30, 0x3c, 0xa0, 0x0e, + 0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, + 0x01, 0x82, 0x37, 0x02, 0x02, 0x0a, 0xa3, 0x2a, + 0x30, 0x28, 0xa0, 0x26, 0x1b, 0x24, 0x6e, 0x6f, + 0x74, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, + 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x52, 0x46, 0x43, + 0x34, 0x31, 0x37, 0x38, 0x40, 0x70, 0x6c, 0x65, + 0x61, 0x73, 0x65, 0x5f, 0x69, 0x67, 0x6e, 0x6f, + 0x72, 0x65 +#endif +}; + +void ksmbd_copy_gss_neg_header(void *buf) +{ + memcpy(buf, NEGOTIATE_GSS_HEADER, AUTH_GSS_LENGTH); +} + +static void +str_to_key(unsigned char *str, unsigned char *key) +{ + int i; + + key[0] = str[0] >> 1; + key[1] = ((str[0] & 0x01) << 6) | (str[1] >> 2); + key[2] = ((str[1] & 0x03) << 5) | (str[2] >> 3); + key[3] = ((str[2] & 0x07) << 4) | (str[3] >> 4); + key[4] = ((str[3] & 0x0F) << 3) | (str[4] >> 5); + key[5] = ((str[4] & 0x1F) << 2) | (str[5] >> 6); + key[6] = ((str[5] & 0x3F) << 1) | (str[6] >> 7); + key[7] = str[6] & 0x7F; + for (i = 0; i < 8; i++) + key[i] = (key[i] << 1); +} + +static int +smbhash(unsigned char *out, const unsigned char *in, unsigned char *key) +{ + unsigned char key2[8]; + struct des_ctx ctx; + + if (fips_enabled) { + ksmbd_debug(AUTH, "FIPS compliance enabled: DES not permitted\n"); + return -ENOENT; + } + + str_to_key(key, key2); + des_expand_key(&ctx, key2, DES_KEY_SIZE); + des_encrypt(&ctx, out, in); + memzero_explicit(&ctx, sizeof(ctx)); + return 0; +} + +static int ksmbd_enc_p24(unsigned char *p21, const unsigned char *c8, unsigned char *p24) +{ + int rc; + + rc = smbhash(p24, c8, p21); + if (rc) + return rc; + rc = smbhash(p24 + 8, c8, p21 + 7); + if (rc) + return rc; + return smbhash(p24 + 16, c8, p21 + 14); +} + +/* produce a md4 message digest from data of length n bytes */ +static int ksmbd_enc_md4(unsigned char *md4_hash, unsigned char *link_str, + int link_len) +{ + int rc; + struct ksmbd_crypto_ctx *ctx; + + ctx = ksmbd_crypto_ctx_find_md4(); + if (!ctx) { + ksmbd_debug(AUTH, "Crypto md4 allocation error\n"); + return -ENOMEM; + } + + rc = crypto_shash_init(CRYPTO_MD4(ctx)); + if (rc) { + ksmbd_debug(AUTH, "Could not init md4 shash\n"); + goto out; + } + + rc = crypto_shash_update(CRYPTO_MD4(ctx), link_str, link_len); + if (rc) { + ksmbd_debug(AUTH, "Could not update with link_str\n"); + goto out; + } + + rc = crypto_shash_final(CRYPTO_MD4(ctx), md4_hash); + if (rc) + ksmbd_debug(AUTH, "Could not generate md4 hash\n"); +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +#ifdef CONFIG_SMB_INSECURE_SERVER +static int ksmbd_enc_update_sess_key(unsigned char *md5_hash, char *nonce, + char *server_challenge, int len) +{ + int rc; + struct ksmbd_crypto_ctx *ctx; + + ctx = ksmbd_crypto_ctx_find_md5(); + if (!ctx) { + ksmbd_debug(AUTH, "Crypto md5 allocation error\n"); + return -ENOMEM; + } + + rc = crypto_shash_init(CRYPTO_MD5(ctx)); + if (rc) { + ksmbd_debug(AUTH, "Could not init md5 shash\n"); + goto out; + } + + rc = crypto_shash_update(CRYPTO_MD5(ctx), server_challenge, len); + if (rc) { + ksmbd_debug(AUTH, "Could not update with challenge\n"); + goto out; + } + + rc = crypto_shash_update(CRYPTO_MD5(ctx), nonce, len); + if (rc) { + ksmbd_debug(AUTH, "Could not update with nonce\n"); + goto out; + } + + rc = crypto_shash_final(CRYPTO_MD5(ctx), md5_hash); + if (rc) + ksmbd_debug(AUTH, "Could not generate md5 hash\n"); +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} +#endif + +/** + * ksmbd_gen_sess_key() - function to generate session key + * @sess: session of connection + * @hash: source hash value to be used for find session key + * @hmac: source hmac value to be used for finding session key + * + */ +static int ksmbd_gen_sess_key(struct ksmbd_session *sess, char *hash, + char *hmac) +{ + struct ksmbd_crypto_ctx *ctx; + int rc; + + ctx = ksmbd_crypto_ctx_find_hmacmd5(); + if (!ctx) { + ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); + return -ENOMEM; + } + + rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), + hash, + CIFS_HMAC_MD5_HASH_SIZE); + if (rc) { + ksmbd_debug(AUTH, "hmacmd5 set key fail error %d\n", rc); + goto out; + } + + rc = crypto_shash_init(CRYPTO_HMACMD5(ctx)); + if (rc) { + ksmbd_debug(AUTH, "could not init hmacmd5 error %d\n", rc); + goto out; + } + + rc = crypto_shash_update(CRYPTO_HMACMD5(ctx), + hmac, + SMB2_NTLMV2_SESSKEY_SIZE); + if (rc) { + ksmbd_debug(AUTH, "Could not update with response error %d\n", rc); + goto out; + } + + rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), sess->sess_key); + if (rc) { + ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n", rc); + goto out; + } + +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +static int calc_ntlmv2_hash(struct ksmbd_conn *conn, struct ksmbd_session *sess, + char *ntlmv2_hash, char *dname) +{ + int ret, len, conv_len; + wchar_t *domain = NULL; + __le16 *uniname = NULL; + struct ksmbd_crypto_ctx *ctx; + + ctx = ksmbd_crypto_ctx_find_hmacmd5(); + if (!ctx) { + ksmbd_debug(AUTH, "can't generate ntlmv2 hash\n"); + return -ENOMEM; + } + + ret = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), + user_passkey(sess->user), + CIFS_ENCPWD_SIZE); + if (ret) { + ksmbd_debug(AUTH, "Could not set NT Hash as a key\n"); + goto out; + } + + ret = crypto_shash_init(CRYPTO_HMACMD5(ctx)); + if (ret) { + ksmbd_debug(AUTH, "could not init hmacmd5\n"); + goto out; + } + + /* convert user_name to unicode */ + len = strlen(user_name(sess->user)); + uniname = kzalloc(2 + UNICODE_LEN(len), GFP_KERNEL); + if (!uniname) { + ret = -ENOMEM; + goto out; + } + + conv_len = smb_strtoUTF16(uniname, user_name(sess->user), len, + conn->local_nls); + if (conv_len < 0 || conv_len > len) { + ret = -EINVAL; + goto out; + } + UniStrupr(uniname); + + ret = crypto_shash_update(CRYPTO_HMACMD5(ctx), + (char *)uniname, + UNICODE_LEN(conv_len)); + if (ret) { + ksmbd_debug(AUTH, "Could not update with user\n"); + goto out; + } + + /* Convert domain name or conn name to unicode and uppercase */ + len = strlen(dname); + domain = kzalloc(2 + UNICODE_LEN(len), GFP_KERNEL); + if (!domain) { + ret = -ENOMEM; + goto out; + } + + conv_len = smb_strtoUTF16((__le16 *)domain, dname, len, + conn->local_nls); + if (conv_len < 0 || conv_len > len) { + ret = -EINVAL; + goto out; + } + + ret = crypto_shash_update(CRYPTO_HMACMD5(ctx), + (char *)domain, + UNICODE_LEN(conv_len)); + if (ret) { + ksmbd_debug(AUTH, "Could not update with domain\n"); + goto out; + } + + ret = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_hash); + if (ret) + ksmbd_debug(AUTH, "Could not generate md5 hash\n"); +out: + kfree(uniname); + kfree(domain); + ksmbd_release_crypto_ctx(ctx); + return ret; +} + +/** + * ksmbd_auth_ntlm() - NTLM authentication handler + * @sess: session of connection + * @pw_buf: NTLM challenge response + * @passkey: user password + * + * Return: 0 on success, error number on error + */ +int ksmbd_auth_ntlm(struct ksmbd_session *sess, char *pw_buf, char *cryptkey) +{ + int rc; + unsigned char p21[21]; + char key[CIFS_AUTH_RESP_SIZE]; + + memset(p21, '\0', 21); + memcpy(p21, user_passkey(sess->user), CIFS_NTHASH_SIZE); + rc = ksmbd_enc_p24(p21, cryptkey, key); + if (rc) { + pr_err("password processing failed\n"); + return rc; + } + + ksmbd_enc_md4(sess->sess_key, user_passkey(sess->user), + CIFS_SMB1_SESSKEY_SIZE); + memcpy(sess->sess_key + CIFS_SMB1_SESSKEY_SIZE, key, + CIFS_AUTH_RESP_SIZE); + sess->sequence_number = 1; + + if (strncmp(pw_buf, key, CIFS_AUTH_RESP_SIZE) != 0) { + ksmbd_debug(AUTH, "ntlmv1 authentication failed\n"); + return -EINVAL; + } + + ksmbd_debug(AUTH, "ntlmv1 authentication pass\n"); + return 0; +} + +/** + * ksmbd_auth_ntlmv2() - NTLMv2 authentication handler + * @sess: session of connection + * @ntlmv2: NTLMv2 challenge response + * @blen: NTLMv2 blob length + * @domain_name: domain name + * + * Return: 0 on success, error number on error + */ +int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, + struct ntlmv2_resp *ntlmv2, int blen, char *domain_name, + char *cryptkey) +{ + char ntlmv2_hash[CIFS_ENCPWD_SIZE]; + char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE]; + struct ksmbd_crypto_ctx *ctx; + char *construct = NULL; + int rc, len; + + ctx = ksmbd_crypto_ctx_find_hmacmd5(); + if (!ctx) { + ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); + return -ENOMEM; + } + + rc = calc_ntlmv2_hash(conn, sess, ntlmv2_hash, domain_name); + if (rc) { + ksmbd_debug(AUTH, "could not get v2 hash rc %d\n", rc); + goto out; + } + + rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx), + ntlmv2_hash, + CIFS_HMAC_MD5_HASH_SIZE); + if (rc) { + ksmbd_debug(AUTH, "Could not set NTLMV2 Hash as a key\n"); + goto out; + } + + rc = crypto_shash_init(CRYPTO_HMACMD5(ctx)); + if (rc) { + ksmbd_debug(AUTH, "Could not init hmacmd5\n"); + goto out; + } + + len = CIFS_CRYPTO_KEY_SIZE + blen; + construct = kzalloc(len, GFP_KERNEL); + if (!construct) { + rc = -ENOMEM; + goto out; + } + + memcpy(construct, cryptkey, CIFS_CRYPTO_KEY_SIZE); + memcpy(construct + CIFS_CRYPTO_KEY_SIZE, &ntlmv2->blob_signature, blen); + + rc = crypto_shash_update(CRYPTO_HMACMD5(ctx), construct, len); + if (rc) { + ksmbd_debug(AUTH, "Could not update with response\n"); + goto out; + } + + rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_rsp); + if (rc) { + ksmbd_debug(AUTH, "Could not generate md5 hash\n"); + goto out; + } + + rc = ksmbd_gen_sess_key(sess, ntlmv2_hash, ntlmv2_rsp); + if (rc) { + ksmbd_debug(AUTH, "Could not generate sess key\n"); + goto out; + } + + if (memcmp(ntlmv2->ntlmv2_hash, ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE) != 0) + rc = -EINVAL; +out: + ksmbd_release_crypto_ctx(ctx); + kfree(construct); + return rc; +} + +#ifdef CONFIG_SMB_INSECURE_SERVER +/** + * __ksmbd_auth_ntlmv2() - NTLM2(extended security) authentication handler + * @sess: session of connection + * @client_nonce: client nonce from LM response. + * @ntlm_resp: ntlm response data from client. + * + * Return: 0 on success, error number on error + */ +static int __ksmbd_auth_ntlmv2(struct ksmbd_session *sess, + char *client_nonce, + char *ntlm_resp, + char *cryptkey) +{ + char sess_key[CIFS_SMB1_SESSKEY_SIZE] = {0}; + int rc; + unsigned char p21[21]; + char key[CIFS_AUTH_RESP_SIZE]; + + rc = ksmbd_enc_update_sess_key(sess_key, client_nonce, cryptkey, 8); + if (rc) { + pr_err("password processing failed\n"); + goto out; + } + + memset(p21, '\0', 21); + memcpy(p21, user_passkey(sess->user), CIFS_NTHASH_SIZE); + rc = ksmbd_enc_p24(p21, sess_key, key); + if (rc) { + pr_err("password processing failed\n"); + goto out; + } + + if (memcmp(ntlm_resp, key, CIFS_AUTH_RESP_SIZE) != 0) + rc = -EINVAL; +out: + return rc; +} +#endif + +static int cifs_arc4_setkey(struct arc4_ctx *ctx, const u8 *in_key, unsigned int key_len) +{ + int i, j = 0, k = 0; + + ctx->x = 1; + ctx->y = 0; + + for (i = 0; i < 256; i++) + ctx->S[i] = i; + + for (i = 0; i < 256; i++) { + u32 a = ctx->S[i]; + + j = (j + in_key[k] + a) & 0xff; + ctx->S[i] = ctx->S[j]; + ctx->S[j] = a; + if (++k >= key_len) + k = 0; + } + + return 0; +} + +static void cifs_arc4_crypt(struct arc4_ctx *ctx, u8 *out, const u8 *in, unsigned int len) +{ + u32 *const S = ctx->S; + u32 x, y, a, b; + u32 ty, ta, tb; + + if (len == 0) + return; + + x = ctx->x; + y = ctx->y; + + a = S[x]; + y = (y + a) & 0xff; + b = S[y]; + + do { + S[y] = a; + a = (a + b) & 0xff; + S[x] = b; + x = (x + 1) & 0xff; + ta = S[x]; + ty = (y + ta) & 0xff; + tb = S[ty]; + *out++ = *in++ ^ S[a]; + if (--len == 0) + break; + y = ty; + a = ta; + b = tb; + } while (true); + + ctx->x = x; + ctx->y = y; +} + +/** + * ksmbd_decode_ntlmssp_auth_blob() - helper function to construct + * authenticate blob + * @authblob: authenticate blob source pointer + * @usr: user details + * @sess: session of connection + * + * Return: 0 on success, error number on error + */ +int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, + int blob_len, struct ksmbd_conn *conn, + struct ksmbd_session *sess) +{ + char *domain_name; + unsigned int nt_off, dn_off; + unsigned short nt_len, dn_len; +#ifdef CONFIG_SMB_INSECURE_SERVER + unsigned int lm_off; + unsigned short lm_len; +#endif + int ret; + + if (blob_len < sizeof(struct authenticate_message)) { + ksmbd_debug(AUTH, "negotiate blob len %d too small\n", + blob_len); + return -EINVAL; + } + + if (memcmp(authblob->Signature, "NTLMSSP", 8)) { + ksmbd_debug(AUTH, "blob signature incorrect %s\n", + authblob->Signature); + return -EINVAL; + } + + nt_off = le32_to_cpu(authblob->NtChallengeResponse.BufferOffset); + nt_len = le16_to_cpu(authblob->NtChallengeResponse.Length); + + dn_off = le32_to_cpu(authblob->DomainName.BufferOffset); + dn_len = le16_to_cpu(authblob->DomainName.Length); + + if (blob_len < (u64)dn_off + dn_len || blob_len < (u64)nt_off + nt_len || + nt_len < CIFS_ENCPWD_SIZE) + return -EINVAL; + +#ifdef CONFIG_SMB_INSECURE_SERVER + lm_off = le32_to_cpu(authblob->LmChallengeResponse.BufferOffset); + lm_len = le16_to_cpu(authblob->LmChallengeResponse.Length); + if (blob_len < (u64)lm_off + lm_len) + return -EINVAL; + + /* process NTLM authentication */ + if (nt_len == CIFS_AUTH_RESP_SIZE) { + if (le32_to_cpu(authblob->NegotiateFlags) & + NTLMSSP_NEGOTIATE_EXTENDED_SEC) + return __ksmbd_auth_ntlmv2(sess, + (char *)authblob + lm_off, + (char *)authblob + nt_off, + conn->ntlmssp.cryptkey); + else + return ksmbd_auth_ntlm(sess, (char *)authblob + + nt_off, conn->ntlmssp.cryptkey); + } +#endif + + /* TODO : use domain name that imported from configuration file */ + domain_name = smb_strndup_from_utf16((const char *)authblob + dn_off, + dn_len, true, conn->local_nls); + if (IS_ERR(domain_name)) + return PTR_ERR(domain_name); + + /* process NTLMv2 authentication */ + ksmbd_debug(AUTH, "decode_ntlmssp_authenticate_blob dname%s\n", + domain_name); + ret = ksmbd_auth_ntlmv2(conn, sess, + (struct ntlmv2_resp *)((char *)authblob + nt_off), + nt_len - CIFS_ENCPWD_SIZE, + domain_name, conn->ntlmssp.cryptkey); + kfree(domain_name); + + /* The recovered secondary session key */ + if (conn->ntlmssp.client_flags & NTLMSSP_NEGOTIATE_KEY_XCH) { + struct arc4_ctx *ctx_arc4; + unsigned int sess_key_off, sess_key_len; + + sess_key_off = le32_to_cpu(authblob->SessionKey.BufferOffset); + sess_key_len = le16_to_cpu(authblob->SessionKey.Length); + + if (blob_len < (u64)sess_key_off + sess_key_len) + return -EINVAL; + + ctx_arc4 = kmalloc(sizeof(*ctx_arc4), GFP_KERNEL); + if (!ctx_arc4) + return -ENOMEM; + + cifs_arc4_setkey(ctx_arc4, sess->sess_key, + SMB2_NTLMV2_SESSKEY_SIZE); + cifs_arc4_crypt(ctx_arc4, sess->sess_key, + (char *)authblob + sess_key_off, sess_key_len); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) + kfree_sensitive(ctx_arc4); +#else + memzero_explicit((void *)ctx_arc4, sizeof(*ctx_arc4)); + kfree(ctx_arc4); +#endif + } + + return ret; +} + +/** + * ksmbd_decode_ntlmssp_neg_blob() - helper function to construct + * negotiate blob + * @negblob: negotiate blob source pointer + * @rsp: response header pointer to be updated + * @sess: session of connection + * + */ +int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob, + int blob_len, struct ksmbd_conn *conn) +{ + if (blob_len < sizeof(struct negotiate_message)) { + ksmbd_debug(AUTH, "negotiate blob len %d too small\n", + blob_len); + return -EINVAL; + } + + if (memcmp(negblob->Signature, "NTLMSSP", 8)) { + ksmbd_debug(AUTH, "blob signature incorrect %s\n", + negblob->Signature); + return -EINVAL; + } + + conn->ntlmssp.client_flags = le32_to_cpu(negblob->NegotiateFlags); + return 0; +} + +/** + * ksmbd_build_ntlmssp_challenge_blob() - helper function to construct + * challenge blob + * @chgblob: challenge blob source pointer to initialize + * @rsp: response header pointer to be updated + * @sess: session of connection + * + */ +unsigned int +ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, + struct ksmbd_conn *conn) +{ + struct target_info *tinfo; + wchar_t *name; + __u8 *target_name; + unsigned int flags, blob_off, blob_len, type, target_info_len = 0; + int len, uni_len, conv_len; + int cflags = conn->ntlmssp.client_flags; + + memcpy(chgblob->Signature, NTLMSSP_SIGNATURE, 8); + chgblob->MessageType = NtLmChallenge; + + flags = NTLMSSP_NEGOTIATE_UNICODE | + NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_TARGET_TYPE_SERVER | + NTLMSSP_NEGOTIATE_TARGET_INFO; + + if (cflags & NTLMSSP_NEGOTIATE_SIGN) { + flags |= NTLMSSP_NEGOTIATE_SIGN; + flags |= cflags & (NTLMSSP_NEGOTIATE_128 | + NTLMSSP_NEGOTIATE_56); + } + + if (cflags & NTLMSSP_NEGOTIATE_SEAL && smb3_encryption_negotiated(conn)) + flags |= NTLMSSP_NEGOTIATE_SEAL; + + if (cflags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) + flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN; + + if (cflags & NTLMSSP_REQUEST_TARGET) + flags |= NTLMSSP_REQUEST_TARGET; + + if (conn->use_spnego && + (cflags & NTLMSSP_NEGOTIATE_EXTENDED_SEC)) + flags |= NTLMSSP_NEGOTIATE_EXTENDED_SEC; + + if (cflags & NTLMSSP_NEGOTIATE_KEY_XCH) + flags |= NTLMSSP_NEGOTIATE_KEY_XCH; + + chgblob->NegotiateFlags = cpu_to_le32(flags); + len = strlen(ksmbd_netbios_name()); + name = kmalloc(2 + UNICODE_LEN(len), GFP_KERNEL); + if (!name) + return -ENOMEM; + + conv_len = smb_strtoUTF16((__le16 *)name, ksmbd_netbios_name(), len, + conn->local_nls); + if (conv_len < 0 || conv_len > len) { + kfree(name); + return -EINVAL; + } + + uni_len = UNICODE_LEN(conv_len); + + blob_off = sizeof(struct challenge_message); + blob_len = blob_off + uni_len; + + chgblob->TargetName.Length = cpu_to_le16(uni_len); + chgblob->TargetName.MaximumLength = cpu_to_le16(uni_len); + chgblob->TargetName.BufferOffset = cpu_to_le32(blob_off); + + /* Initialize random conn challenge */ + get_random_bytes(conn->ntlmssp.cryptkey, sizeof(__u64)); + memcpy(chgblob->Challenge, conn->ntlmssp.cryptkey, + CIFS_CRYPTO_KEY_SIZE); + + /* Add Target Information to security buffer */ + chgblob->TargetInfoArray.BufferOffset = cpu_to_le32(blob_len); + + target_name = (__u8 *)chgblob + blob_off; + memcpy(target_name, name, uni_len); + tinfo = (struct target_info *)(target_name + uni_len); + + chgblob->TargetInfoArray.Length = 0; + /* Add target info list for NetBIOS/DNS settings */ + for (type = NTLMSSP_AV_NB_COMPUTER_NAME; + type <= NTLMSSP_AV_DNS_DOMAIN_NAME; type++) { + tinfo->Type = cpu_to_le16(type); + tinfo->Length = cpu_to_le16(uni_len); + memcpy(tinfo->Content, name, uni_len); + tinfo = (struct target_info *)((char *)tinfo + 4 + uni_len); + target_info_len += 4 + uni_len; + } + + /* Add terminator subblock */ + tinfo->Type = 0; + tinfo->Length = 0; + target_info_len += 4; + + chgblob->TargetInfoArray.Length = cpu_to_le16(target_info_len); + chgblob->TargetInfoArray.MaximumLength = cpu_to_le16(target_info_len); + blob_len += target_info_len; + kfree(name); + ksmbd_debug(AUTH, "NTLMSSP SecurityBufferLength %d\n", blob_len); + return blob_len; +} + +#ifdef CONFIG_SMB_SERVER_KERBEROS5 +int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, + int in_len, char *out_blob, int *out_len) +{ + struct ksmbd_spnego_authen_response *resp; + struct ksmbd_user *user = NULL; + int retval; + + resp = ksmbd_ipc_spnego_authen_request(in_blob, in_len); + if (!resp) { + ksmbd_debug(AUTH, "SPNEGO_AUTHEN_REQUEST failure\n"); + return -EINVAL; + } + + if (!(resp->login_response.status & KSMBD_USER_FLAG_OK)) { + ksmbd_debug(AUTH, "krb5 authentication failure\n"); + retval = -EPERM; + goto out; + } + + if (*out_len <= resp->spnego_blob_len) { + ksmbd_debug(AUTH, "buf len %d, but blob len %d\n", + *out_len, resp->spnego_blob_len); + retval = -EINVAL; + goto out; + } + + if (resp->session_key_len > sizeof(sess->sess_key)) { + ksmbd_debug(AUTH, "session key is too long\n"); + retval = -EINVAL; + goto out; + } + + user = ksmbd_alloc_user(&resp->login_response); + if (!user) { + ksmbd_debug(AUTH, "login failure\n"); + retval = -ENOMEM; + goto out; + } + sess->user = user; + + memcpy(sess->sess_key, resp->payload, resp->session_key_len); + memcpy(out_blob, resp->payload + resp->session_key_len, + resp->spnego_blob_len); + *out_len = resp->spnego_blob_len; + retval = 0; +out: + kvfree(resp); + return retval; +} +#else +int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, + int in_len, char *out_blob, int *out_len) +{ + return -EOPNOTSUPP; +} +#endif + +#ifdef CONFIG_SMB_INSECURE_SERVER +/** + * ksmbd_sign_smb1_pdu() - function to generate SMB1 packet signing + * @sess: session of connection + * @iov: buffer iov array + * @n_vec: number of iovecs + * @sig: signature value generated for client request packet + * + */ +int ksmbd_sign_smb1_pdu(struct ksmbd_session *sess, struct kvec *iov, int n_vec, + char *sig) +{ + struct ksmbd_crypto_ctx *ctx; + int rc, i; + + ctx = ksmbd_crypto_ctx_find_md5(); + if (!ctx) { + ksmbd_debug(AUTH, "could not crypto alloc md5\n"); + return -ENOMEM; + } + + rc = crypto_shash_init(CRYPTO_MD5(ctx)); + if (rc) { + ksmbd_debug(AUTH, "md5 init error %d\n", rc); + goto out; + } + + rc = crypto_shash_update(CRYPTO_MD5(ctx), sess->sess_key, 40); + if (rc) { + ksmbd_debug(AUTH, "md5 update error %d\n", rc); + goto out; + } + + for (i = 0; i < n_vec; i++) { + rc = crypto_shash_update(CRYPTO_MD5(ctx), + iov[i].iov_base, + iov[i].iov_len); + if (rc) { + ksmbd_debug(AUTH, "md5 update error %d\n", rc); + goto out; + } + } + + rc = crypto_shash_final(CRYPTO_MD5(ctx), sig); + if (rc) + ksmbd_debug(AUTH, "md5 generation error %d\n", rc); + +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} +#endif + +/** + * ksmbd_sign_smb2_pdu() - function to generate packet signing + * @conn: connection + * @key: signing key + * @iov: buffer iov array + * @n_vec: number of iovecs + * @sig: signature value generated for client request packet + * + */ +int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, + int n_vec, char *sig) +{ + struct ksmbd_crypto_ctx *ctx; + int rc, i; + + ctx = ksmbd_crypto_ctx_find_hmacsha256(); + if (!ctx) { + ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); + return -ENOMEM; + } + + rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx), + key, + SMB2_NTLMV2_SESSKEY_SIZE); + if (rc) + goto out; + + rc = crypto_shash_init(CRYPTO_HMACSHA256(ctx)); + if (rc) { + ksmbd_debug(AUTH, "hmacsha256 init error %d\n", rc); + goto out; + } + + for (i = 0; i < n_vec; i++) { + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), + iov[i].iov_base, + iov[i].iov_len); + if (rc) { + ksmbd_debug(AUTH, "hmacsha256 update error %d\n", rc); + goto out; + } + } + + rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), sig); + if (rc) + ksmbd_debug(AUTH, "hmacsha256 generation error %d\n", rc); +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +/** + * ksmbd_sign_smb3_pdu() - function to generate packet signing + * @conn: connection + * @key: signing key + * @iov: buffer iov array + * @n_vec: number of iovecs + * @sig: signature value generated for client request packet + * + */ +int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, + int n_vec, char *sig) +{ + struct ksmbd_crypto_ctx *ctx; + int rc, i; + + ctx = ksmbd_crypto_ctx_find_cmacaes(); + if (!ctx) { + ksmbd_debug(AUTH, "could not crypto alloc cmac\n"); + return -ENOMEM; + } + + rc = crypto_shash_setkey(CRYPTO_CMACAES_TFM(ctx), + key, + SMB2_CMACAES_SIZE); + if (rc) + goto out; + + rc = crypto_shash_init(CRYPTO_CMACAES(ctx)); + if (rc) { + ksmbd_debug(AUTH, "cmaces init error %d\n", rc); + goto out; + } + + for (i = 0; i < n_vec; i++) { + rc = crypto_shash_update(CRYPTO_CMACAES(ctx), + iov[i].iov_base, + iov[i].iov_len); + if (rc) { + ksmbd_debug(AUTH, "cmaces update error %d\n", rc); + goto out; + } + } + + rc = crypto_shash_final(CRYPTO_CMACAES(ctx), sig); + if (rc) + ksmbd_debug(AUTH, "cmaces generation error %d\n", rc); +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +struct derivation { + struct kvec label; + struct kvec context; + bool binding; +}; + +static int generate_key(struct ksmbd_conn *conn, struct ksmbd_session *sess, + struct kvec label, struct kvec context, __u8 *key, + unsigned int key_size) +{ + unsigned char zero = 0x0; + __u8 i[4] = {0, 0, 0, 1}; + __u8 L128[4] = {0, 0, 0, 128}; + __u8 L256[4] = {0, 0, 1, 0}; + int rc; + unsigned char prfhash[SMB2_HMACSHA256_SIZE]; + unsigned char *hashptr = prfhash; + struct ksmbd_crypto_ctx *ctx; + + memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE); + memset(key, 0x0, key_size); + + ctx = ksmbd_crypto_ctx_find_hmacsha256(); + if (!ctx) { + ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n"); + return -ENOMEM; + } + + rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx), + sess->sess_key, + SMB2_NTLMV2_SESSKEY_SIZE); + if (rc) + goto smb3signkey_ret; + + rc = crypto_shash_init(CRYPTO_HMACSHA256(ctx)); + if (rc) { + ksmbd_debug(AUTH, "hmacsha256 init error %d\n", rc); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), i, 4); + if (rc) { + ksmbd_debug(AUTH, "could not update with n\n"); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), + label.iov_base, + label.iov_len); + if (rc) { + ksmbd_debug(AUTH, "could not update with label\n"); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), &zero, 1); + if (rc) { + ksmbd_debug(AUTH, "could not update with zero\n"); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), + context.iov_base, + context.iov_len); + if (rc) { + ksmbd_debug(AUTH, "could not update with context\n"); + goto smb3signkey_ret; + } + + if (conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || + conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L256, 4); + else + rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L128, 4); + if (rc) { + ksmbd_debug(AUTH, "could not update with L\n"); + goto smb3signkey_ret; + } + + rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), hashptr); + if (rc) { + ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n", + rc); + goto smb3signkey_ret; + } + + memcpy(key, hashptr, key_size); + +smb3signkey_ret: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +static int generate_smb3signingkey(struct ksmbd_session *sess, + struct ksmbd_conn *conn, + const struct derivation *signing) +{ + int rc; + struct channel *chann; + char *key; + + chann = lookup_chann_list(sess, conn); + if (!chann) + return 0; + + if (conn->dialect >= SMB30_PROT_ID && signing->binding) + key = chann->smb3signingkey; + else + key = sess->smb3signingkey; + + rc = generate_key(conn, sess, signing->label, signing->context, key, + SMB3_SIGN_KEY_SIZE); + if (rc) + return rc; + + if (!(conn->dialect >= SMB30_PROT_ID && signing->binding)) + memcpy(chann->smb3signingkey, key, SMB3_SIGN_KEY_SIZE); + + ksmbd_debug(AUTH, "dumping generated AES signing keys\n"); + ksmbd_debug(AUTH, "Session Id %llu\n", sess->id); + ksmbd_debug(AUTH, "Session Key %*ph\n", + SMB2_NTLMV2_SESSKEY_SIZE, sess->sess_key); + ksmbd_debug(AUTH, "Signing Key %*ph\n", + SMB3_SIGN_KEY_SIZE, key); + return 0; +} + +int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess, + struct ksmbd_conn *conn) +{ + struct derivation d; + + d.label.iov_base = "SMB2AESCMAC"; + d.label.iov_len = 12; + d.context.iov_base = "SmbSign"; + d.context.iov_len = 8; + d.binding = conn->binding; + + return generate_smb3signingkey(sess, conn, &d); +} + +int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess, + struct ksmbd_conn *conn) +{ + struct derivation d; + + d.label.iov_base = "SMBSigningKey"; + d.label.iov_len = 14; + if (conn->binding) { + struct preauth_session *preauth_sess; + + preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id); + if (!preauth_sess) + return -ENOENT; + d.context.iov_base = preauth_sess->Preauth_HashValue; + } else { + d.context.iov_base = sess->Preauth_HashValue; + } + d.context.iov_len = 64; + d.binding = conn->binding; + + return generate_smb3signingkey(sess, conn, &d); +} + +struct derivation_twin { + struct derivation encryption; + struct derivation decryption; +}; + +static int generate_smb3encryptionkey(struct ksmbd_conn *conn, + struct ksmbd_session *sess, + const struct derivation_twin *ptwin) +{ + int rc; + + rc = generate_key(conn, sess, ptwin->encryption.label, + ptwin->encryption.context, sess->smb3encryptionkey, + SMB3_ENC_DEC_KEY_SIZE); + if (rc) + return rc; + + rc = generate_key(conn, sess, ptwin->decryption.label, + ptwin->decryption.context, + sess->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE); + if (rc) + return rc; + + ksmbd_debug(AUTH, "dumping generated AES encryption keys\n"); + ksmbd_debug(AUTH, "Cipher type %d\n", conn->cipher_type); + ksmbd_debug(AUTH, "Session Id %llu\n", sess->id); + ksmbd_debug(AUTH, "Session Key %*ph\n", + SMB2_NTLMV2_SESSKEY_SIZE, sess->sess_key); + if (conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || + conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) { + ksmbd_debug(AUTH, "ServerIn Key %*ph\n", + SMB3_GCM256_CRYPTKEY_SIZE, sess->smb3encryptionkey); + ksmbd_debug(AUTH, "ServerOut Key %*ph\n", + SMB3_GCM256_CRYPTKEY_SIZE, sess->smb3decryptionkey); + } else { + ksmbd_debug(AUTH, "ServerIn Key %*ph\n", + SMB3_GCM128_CRYPTKEY_SIZE, sess->smb3encryptionkey); + ksmbd_debug(AUTH, "ServerOut Key %*ph\n", + SMB3_GCM128_CRYPTKEY_SIZE, sess->smb3decryptionkey); + } + return 0; +} + +int ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn, + struct ksmbd_session *sess) +{ + struct derivation_twin twin; + struct derivation *d; + + d = &twin.encryption; + d->label.iov_base = "SMB2AESCCM"; + d->label.iov_len = 11; + d->context.iov_base = "ServerOut"; + d->context.iov_len = 10; + + d = &twin.decryption; + d->label.iov_base = "SMB2AESCCM"; + d->label.iov_len = 11; + d->context.iov_base = "ServerIn "; + d->context.iov_len = 10; + + return generate_smb3encryptionkey(conn, sess, &twin); +} + +int ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn, + struct ksmbd_session *sess) +{ + struct derivation_twin twin; + struct derivation *d; + + d = &twin.encryption; + d->label.iov_base = "SMBS2CCipherKey"; + d->label.iov_len = 16; + d->context.iov_base = sess->Preauth_HashValue; + d->context.iov_len = 64; + + d = &twin.decryption; + d->label.iov_base = "SMBC2SCipherKey"; + d->label.iov_len = 16; + d->context.iov_base = sess->Preauth_HashValue; + d->context.iov_len = 64; + + return generate_smb3encryptionkey(conn, sess, &twin); +} + +int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, + __u8 *pi_hash) +{ + int rc; + struct smb2_hdr *rcv_hdr = smb2_get_msg(buf); + char *all_bytes_msg = (char *)&rcv_hdr->ProtocolId; + int msg_size = get_rfc1002_len(buf); + struct ksmbd_crypto_ctx *ctx = NULL; + + if (conn->preauth_info->Preauth_HashId != + SMB2_PREAUTH_INTEGRITY_SHA512) + return -EINVAL; + + ctx = ksmbd_crypto_ctx_find_sha512(); + if (!ctx) { + ksmbd_debug(AUTH, "could not alloc sha512\n"); + return -ENOMEM; + } + + rc = crypto_shash_init(CRYPTO_SHA512(ctx)); + if (rc) { + ksmbd_debug(AUTH, "could not init shashn"); + goto out; + } + + rc = crypto_shash_update(CRYPTO_SHA512(ctx), pi_hash, 64); + if (rc) { + ksmbd_debug(AUTH, "could not update with n\n"); + goto out; + } + + rc = crypto_shash_update(CRYPTO_SHA512(ctx), all_bytes_msg, msg_size); + if (rc) { + ksmbd_debug(AUTH, "could not update with n\n"); + goto out; + } + + rc = crypto_shash_final(CRYPTO_SHA512(ctx), pi_hash); + if (rc) { + ksmbd_debug(AUTH, "Could not generate hash err : %d\n", rc); + goto out; + } +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +int ksmbd_gen_sd_hash(struct ksmbd_conn *conn, char *sd_buf, int len, + __u8 *pi_hash) +{ + int rc; + struct ksmbd_crypto_ctx *ctx = NULL; + + ctx = ksmbd_crypto_ctx_find_sha256(); + if (!ctx) { + ksmbd_debug(AUTH, "could not alloc sha256\n"); + return -ENOMEM; + } + + rc = crypto_shash_init(CRYPTO_SHA256(ctx)); + if (rc) { + ksmbd_debug(AUTH, "could not init shashn"); + goto out; + } + + rc = crypto_shash_update(CRYPTO_SHA256(ctx), sd_buf, len); + if (rc) { + ksmbd_debug(AUTH, "could not update with n\n"); + goto out; + } + + rc = crypto_shash_final(CRYPTO_SHA256(ctx), pi_hash); + if (rc) { + ksmbd_debug(AUTH, "Could not generate hash err : %d\n", rc); + goto out; + } +out: + ksmbd_release_crypto_ctx(ctx); + return rc; +} + +static int ksmbd_get_encryption_key(struct ksmbd_work *work, __u64 ses_id, + int enc, u8 *key) +{ + struct ksmbd_session *sess; + u8 *ses_enc_key; + + if (enc) + sess = work->sess; + else + sess = ksmbd_session_lookup_all(work->conn, ses_id); + if (!sess) + return -EINVAL; + + ses_enc_key = enc ? sess->smb3encryptionkey : + sess->smb3decryptionkey; + memcpy(key, ses_enc_key, SMB3_ENC_DEC_KEY_SIZE); + + return 0; +} + +static inline void smb2_sg_set_buf(struct scatterlist *sg, const void *buf, + unsigned int buflen) +{ + void *addr; + + if (is_vmalloc_addr(buf)) + addr = vmalloc_to_page(buf); + else + addr = virt_to_page(buf); + sg_set_page(sg, addr, buflen, offset_in_page(buf)); +} + +static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec, + u8 *sign) +{ + struct scatterlist *sg; + unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; + int i, nr_entries[3] = {0}, total_entries = 0, sg_idx = 0; + + if (!nvec) + return NULL; + + for (i = 0; i < nvec - 1; i++) { + unsigned long kaddr = (unsigned long)iov[i + 1].iov_base; + + if (is_vmalloc_addr(iov[i + 1].iov_base)) { + nr_entries[i] = ((kaddr + iov[i + 1].iov_len + + PAGE_SIZE - 1) >> PAGE_SHIFT) - + (kaddr >> PAGE_SHIFT); + } else { + nr_entries[i]++; + } + total_entries += nr_entries[i]; + } + + /* Add two entries for transform header and signature */ + total_entries += 2; + + sg = kmalloc_array(total_entries, sizeof(struct scatterlist), GFP_KERNEL); + if (!sg) + return NULL; + + sg_init_table(sg, total_entries); + smb2_sg_set_buf(&sg[sg_idx++], iov[0].iov_base + 24, assoc_data_len); + for (i = 0; i < nvec - 1; i++) { + void *data = iov[i + 1].iov_base; + int len = iov[i + 1].iov_len; + + if (is_vmalloc_addr(data)) { + int j, offset = offset_in_page(data); + + for (j = 0; j < nr_entries[i]; j++) { + unsigned int bytes = PAGE_SIZE - offset; + + if (!len) + break; + + if (bytes > len) + bytes = len; + + sg_set_page(&sg[sg_idx++], + vmalloc_to_page(data), bytes, + offset_in_page(data)); + + data += bytes; + len -= bytes; + offset = 0; + } + } else { + sg_set_page(&sg[sg_idx++], virt_to_page(data), len, + offset_in_page(data)); + } + } + smb2_sg_set_buf(&sg[sg_idx], sign, SMB2_SIGNATURE_SIZE); + return sg; +} + +int ksmbd_crypt_message(struct ksmbd_work *work, struct kvec *iov, + unsigned int nvec, int enc) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_transform_hdr *tr_hdr = smb2_get_msg(iov[0].iov_base); + unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; + int rc; + struct scatterlist *sg; + u8 sign[SMB2_SIGNATURE_SIZE] = {}; + u8 key[SMB3_ENC_DEC_KEY_SIZE]; + struct aead_request *req; + char *iv; + unsigned int iv_len; + struct crypto_aead *tfm; + unsigned int crypt_len = le32_to_cpu(tr_hdr->OriginalMessageSize); + struct ksmbd_crypto_ctx *ctx; + + rc = ksmbd_get_encryption_key(work, + le64_to_cpu(tr_hdr->SessionId), + enc, + key); + if (rc) { + pr_err("Could not get %scryption key\n", enc ? "en" : "de"); + return rc; + } + + if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM || + conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) + ctx = ksmbd_crypto_ctx_find_gcm(); + else + ctx = ksmbd_crypto_ctx_find_ccm(); + if (!ctx) { + pr_err("crypto alloc failed\n"); + return -ENOMEM; + } + + if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM || + conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) + tfm = CRYPTO_GCM(ctx); + else + tfm = CRYPTO_CCM(ctx); + + if (conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM || + conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) + rc = crypto_aead_setkey(tfm, key, SMB3_GCM256_CRYPTKEY_SIZE); + else + rc = crypto_aead_setkey(tfm, key, SMB3_GCM128_CRYPTKEY_SIZE); + if (rc) { + pr_err("Failed to set aead key %d\n", rc); + goto free_ctx; + } + + rc = crypto_aead_setauthsize(tfm, SMB2_SIGNATURE_SIZE); + if (rc) { + pr_err("Failed to set authsize %d\n", rc); + goto free_ctx; + } + + req = aead_request_alloc(tfm, GFP_KERNEL); + if (!req) { + rc = -ENOMEM; + goto free_ctx; + } + + if (!enc) { + memcpy(sign, &tr_hdr->Signature, SMB2_SIGNATURE_SIZE); + crypt_len += SMB2_SIGNATURE_SIZE; + } + + sg = ksmbd_init_sg(iov, nvec, sign); + if (!sg) { + pr_err("Failed to init sg\n"); + rc = -ENOMEM; + goto free_req; + } + + iv_len = crypto_aead_ivsize(tfm); + iv = kzalloc(iv_len, GFP_KERNEL); + if (!iv) { + rc = -ENOMEM; + goto free_sg; + } + + if (conn->cipher_type == SMB2_ENCRYPTION_AES128_GCM || + conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM) { + memcpy(iv, (char *)tr_hdr->Nonce, SMB3_AES_GCM_NONCE); + } else { + iv[0] = 3; + memcpy(iv + 1, (char *)tr_hdr->Nonce, SMB3_AES_CCM_NONCE); + } + + aead_request_set_crypt(req, sg, sg, crypt_len, iv); + aead_request_set_ad(req, assoc_data_len); + aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL); + + if (enc) + rc = crypto_aead_encrypt(req); + else + rc = crypto_aead_decrypt(req); + if (rc) + goto free_iv; + + if (enc) + memcpy(&tr_hdr->Signature, sign, SMB2_SIGNATURE_SIZE); + +free_iv: + kfree(iv); +free_sg: + kfree(sg); +free_req: + kfree(req); +free_ctx: + ksmbd_release_crypto_ctx(ctx); + return rc; +} diff --git a/fs/ksmbd/auth.h b/fs/ksmbd/auth.h new file mode 100644 index 0000000000000..caba4341988b4 --- /dev/null +++ b/fs/ksmbd/auth.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __AUTH_H__ +#define __AUTH_H__ + +#include "ntlmssp.h" + +#ifdef CONFIG_SMB_SERVER_KERBEROS5 +#define AUTH_GSS_LENGTH 96 +#define AUTH_GSS_PADDING 0 +#else +#define AUTH_GSS_LENGTH 74 +#define AUTH_GSS_PADDING 6 +#endif + +#define CIFS_HMAC_MD5_HASH_SIZE (16) +#define CIFS_NTHASH_SIZE (16) + +/* + * Size of the ntlm client response + */ +#define CIFS_AUTH_RESP_SIZE 24 +#define CIFS_SMB1_SIGNATURE_SIZE 8 +#define CIFS_SMB1_SESSKEY_SIZE 16 + +#define KSMBD_AUTH_NTLMSSP 0x0001 +#define KSMBD_AUTH_KRB5 0x0002 +#define KSMBD_AUTH_MSKRB5 0x0004 +#define KSMBD_AUTH_KRB5U2U 0x0008 + +struct ksmbd_session; +struct ksmbd_conn; +struct ksmbd_work; +struct kvec; + +int ksmbd_crypt_message(struct ksmbd_work *work, struct kvec *iov, + unsigned int nvec, int enc); +void ksmbd_copy_gss_neg_header(void *buf); +int ksmbd_auth_ntlm(struct ksmbd_session *sess, char *pw_buf, char *cryptkey); +int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, + struct ntlmv2_resp *ntlmv2, int blen, char *domain_name, + char *cryptkey); +int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, + int blob_len, struct ksmbd_conn *conn, + struct ksmbd_session *sess); +int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob, + int blob_len, struct ksmbd_conn *conn); +unsigned int +ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob, + struct ksmbd_conn *conn); +int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob, + int in_len, char *out_blob, int *out_len); +#ifdef CONFIG_SMB_INSECURE_SERVER +int ksmbd_sign_smb1_pdu(struct ksmbd_session *sess, struct kvec *iov, int n_vec, + char *sig); +#endif +int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, + int n_vec, char *sig); +int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov, + int n_vec, char *sig); +int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess, + struct ksmbd_conn *conn); +int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess, + struct ksmbd_conn *conn); +int ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn, + struct ksmbd_session *sess); +int ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn, + struct ksmbd_session *sess); +int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf, + __u8 *pi_hash); +int ksmbd_gen_sd_hash(struct ksmbd_conn *conn, char *sd_buf, int len, + __u8 *pi_hash); + +#define ARC4_MIN_KEY_SIZE 1 +#define ARC4_MAX_KEY_SIZE 256 +#define ARC4_BLOCK_SIZE 1 + +struct arc4_ctx { + u32 S[256]; + u32 x, y; +}; +#endif diff --git a/fs/ksmbd/build_ksmbd.sh b/fs/ksmbd/build_ksmbd.sh new file mode 100755 index 0000000000000..4b10f0417d84a --- /dev/null +++ b/fs/ksmbd/build_ksmbd.sh @@ -0,0 +1,177 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2019 Samsung Electronics Co., Ltd. +# + +#!/bin/sh + +KERNEL_SRC='' +COMP_FLAGS='' + +function is_module +{ + local ok=$(cat "$KERNEL_SRC"/.config | grep "CONFIG_SMB_SERVER=m") + + if [ "z$ok" == "z" ]; then + echo "1" + return 1 + fi + + echo "0" + return 0 +} + +function patch_fs_config +{ + local ok=$(pwd | grep -c "fs/smbd") + if [ "z$ok" != "z1" ]; then + echo "ERROR: please ``cd`` to fs/smbd" + exit 1 + fi + + KERNEL_SRC=$(pwd | sed -e 's/fs\/smbd//') + if [ ! -f "$KERNEL_SRC"/fs/Kconfig ]; then + echo "ERROR: please ``cd`` to fs/smbd" + exit 1 + fi + + ok=$(cat "$KERNEL_SRC"/fs/Makefile | grep smbd) + if [ "z$ok" == "z" ]; then + echo 'obj-$(CONFIG_SMB_SERVER) += smbd/' \ + >> "$KERNEL_SRC"/fs/Makefile + fi + + ok=$(cat "$KERNEL_SRC"/fs/Kconfig | grep smbd) + if [ "z$ok" == "z" ]; then + ok=$(cat "$KERNEL_SRC"/fs/Kconfig \ + | sed -e 's/fs\/cifs\/Kconfig/fs\/cifs\/Kconfig\"\nsource \"fs\/smbd\/Kconfig/' \ + > "$KERNEL_SRC"/fs/Kconfig.new) + if [ $? != 0 ]; then + exit 1 + fi + mv "$KERNEL_SRC"/fs/Kconfig.new "$KERNEL_SRC"/fs/Kconfig + fi + + ok=$(cat "$KERNEL_SRC"/.config | grep "CONFIG_NETWORK_FILESYSTEMS=y") + if [ "z$ok" == "z" ]; then + ok=$(echo "CONFIG_NETWORK_FILESYSTEMS=y" \ + >> "$KERNEL_SRC"/.config) + if [ $? != 0 ]; then + exit 1 + fi + fi + + ok=$(is_module) + if [ "z$ok" == "z1" ]; then + ok=$(echo "CONFIG_SMB_SERVER=m" >> "$KERNEL_SRC"/.config) + if [ $? != 0 ]; then + exit 1 + fi + ok=$(echo "CONFIG_SMB_INSECURE_SERVER=y" \ + >> "$KERNEL_SRC"/.config) + if [ $? != 0 ]; then + exit 1 + fi + fi +} + +function ksmbd_module_make +{ + echo "Running smbd make" + + local c="make "$COMP_FLAGS" -C "$KERNEL_SRC" M="$KERNEL_SRC"/fs/smbd" + + rm smbd.ko + + cd "$KERNEL_SRC" + echo $c + $c + cd "$KERNEL_SRC"/fs/smbd + + if [ $? != 0 ]; then + exit 1 + fi +} + +function ksmbd_module_install +{ + echo "Running smbd install" + + local ok=$(lsmod | grep -c smbd) + if [ "z$ok" == "z1" ]; then + sudo rmmod smbd + if [ $? -ne 0 ]; then + echo "ERROR: unable to rmmod smbd" + exit 1 + fi + fi + + ok=$(is_module) + if [ "z$ok" == "z1" ]; then + echo "It doesn't look like SMB_SERVER is as a kernel module" + exit 1 + fi + + if [ ! -f "$KERNEL_SRC"/fs/smbd/smbd.ko ]; then + echo "ERROR: smbd.ko was not found" + exit 1 + fi + + cd "$KERNEL_SRC" + if [ -f "/lib/modules/$(uname -r)/kernel/fs/smbd/smbd.ko*" ]; then + sudo rm /lib/modules/$(uname -r)/kernel/fs/smbd/smbd.ko* + sudo cp "$KERNEL_SRC"/fs/smbd/smbd.ko \ + /lib/modules/$(uname -r)/kernel/fs/smbd/smbd.ko + + local VER=$(make kernelrelease) + sudo depmod -A $VER + else + sudo make -C "$KERNEL_SRC" M="$KERNEL_SRC"/fs/smbd/ \ + modules_install + local VER=$(make kernelrelease) + sudo depmod -A $VER + fi + cd "$KERNEL_SRC"/fs/smbd +} + +function ksmbd_module_clean +{ + echo "Running smbd clean" + + cd "$KERNEL_SRC" + make -C "$KERNEL_SRC" M="$KERNEL_SRC"/fs/smbd/ clean + cd "$KERNEL_SRC"/fs/smbd +} + +function main +{ + patch_fs_config + + COMP_FLAGS="$FLAGS" + + case $1 in + clean) + ksmbd_module_clean + exit 0 + ;; + install) + ksmbd_module_make + ksmbd_module_install + exit 0 + ;; + make) + ksmbd_module_make + exit 0 + ;; + help) + echo "Usage: build_ksmbd.sh [clean | make | install]" + exit 0 + ;; + *) + ksmbd_module_make + exit 0 + ;; + esac +} + +main $1 diff --git a/fs/ksmbd/connection.c b/fs/ksmbd/connection.c new file mode 100644 index 0000000000000..5962bbddf6e24 --- /dev/null +++ b/fs/ksmbd/connection.c @@ -0,0 +1,470 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include + +#include "server.h" +#include "smb_common.h" +#ifdef CONFIG_SMB_INSECURE_SERVER +#include "smb1pdu.h" +#endif +#include "mgmt/ksmbd_ida.h" +#include "connection.h" +#include "transport_tcp.h" +#include "transport_rdma.h" + +static DEFINE_MUTEX(init_lock); + +static struct ksmbd_conn_ops default_conn_ops; + +LIST_HEAD(conn_list); +DEFINE_RWLOCK(conn_list_lock); + +/** + * ksmbd_conn_free() - free resources of the connection instance + * + * @conn: connection instance to be cleand up + * + * During the thread termination, the corresponding conn instance + * resources(sock/memory) are released and finally the conn object is freed. + */ +void ksmbd_conn_free(struct ksmbd_conn *conn) +{ + write_lock(&conn_list_lock); + list_del(&conn->conns_list); + write_unlock(&conn_list_lock); + + xa_destroy(&conn->sessions); + kvfree(conn->request_buf); + kfree(conn->preauth_info); + kfree(conn); +} + +/** + * ksmbd_conn_alloc() - initialize a new connection instance + * + * Return: ksmbd_conn struct on success, otherwise NULL + */ +struct ksmbd_conn *ksmbd_conn_alloc(void) +{ + struct ksmbd_conn *conn; + + conn = kzalloc(sizeof(struct ksmbd_conn), GFP_KERNEL); + if (!conn) + return NULL; + + conn->need_neg = true; + conn->status = KSMBD_SESS_NEW; + conn->local_nls = load_nls("utf8"); + if (!conn->local_nls) + conn->local_nls = load_nls_default(); + if (IS_ENABLED(CONFIG_UNICODE)) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0) + conn->um = utf8_load(UNICODE_AGE(12, 1, 0)); +#else + conn->um = utf8_load("12.1.0"); +#endif + else + conn->um = ERR_PTR(-EOPNOTSUPP); + if (IS_ERR(conn->um)) + conn->um = NULL; + atomic_set(&conn->req_running, 0); + atomic_set(&conn->r_count, 0); + conn->total_credits = 1; + conn->outstanding_credits = 0; + + init_waitqueue_head(&conn->req_running_q); + init_waitqueue_head(&conn->r_count_q); + INIT_LIST_HEAD(&conn->conns_list); + INIT_LIST_HEAD(&conn->requests); + INIT_LIST_HEAD(&conn->async_requests); + spin_lock_init(&conn->request_lock); + spin_lock_init(&conn->credits_lock); + ida_init(&conn->async_ida); + xa_init(&conn->sessions); + + spin_lock_init(&conn->llist_lock); + INIT_LIST_HEAD(&conn->lock_list); + + write_lock(&conn_list_lock); + list_add(&conn->conns_list, &conn_list); + write_unlock(&conn_list_lock); + return conn; +} + +bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c) +{ + struct ksmbd_conn *t; + bool ret = false; + + read_lock(&conn_list_lock); + list_for_each_entry(t, &conn_list, conns_list) { + if (memcmp(t->ClientGUID, c->ClientGUID, SMB2_CLIENT_GUID_SIZE)) + continue; + + ret = true; + break; + } + read_unlock(&conn_list_lock); + return ret; +} + +void ksmbd_conn_enqueue_request(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct list_head *requests_queue = NULL; +#ifdef CONFIG_SMB_INSECURE_SERVER + struct smb2_hdr *hdr = work->request_buf; + + if (hdr->ProtocolId == SMB2_PROTO_NUMBER) { + if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE) { + requests_queue = &conn->requests; + work->synchronous = true; + } + } else { + if (conn->ops->get_cmd_val(work) != SMB_COM_NT_CANCEL) + requests_queue = &conn->requests; + } +#else + if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE) { + requests_queue = &conn->requests; + work->synchronous = true; + } +#endif + + if (requests_queue) { + atomic_inc(&conn->req_running); + spin_lock(&conn->request_lock); + list_add_tail(&work->request_entry, requests_queue); + spin_unlock(&conn->request_lock); + } +} + +int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + int ret = 1; + + if (list_empty(&work->request_entry) && + list_empty(&work->async_request_entry)) + return 0; + + if (!work->multiRsp) + atomic_dec(&conn->req_running); + spin_lock(&conn->request_lock); + if (!work->multiRsp) { + list_del_init(&work->request_entry); + if (!work->synchronous) + list_del_init(&work->async_request_entry); + ret = 0; + } + spin_unlock(&conn->request_lock); + + wake_up_all(&conn->req_running_q); + return ret; +} + +static void ksmbd_conn_lock(struct ksmbd_conn *conn) +{ + mutex_lock(&conn->srv_mutex); +} + +static void ksmbd_conn_unlock(struct ksmbd_conn *conn) +{ + mutex_unlock(&conn->srv_mutex); +} + +void ksmbd_conn_wait_idle(struct ksmbd_conn *conn) +{ + wait_event(conn->req_running_q, atomic_read(&conn->req_running) < 2); +} + +int ksmbd_conn_write(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + size_t len = 0; + int sent; + struct kvec iov[3]; + int iov_idx = 0; + + if (!work->response_buf) { + pr_err("NULL response header\n"); + return -EINVAL; + } + + if (work->tr_buf) { + iov[iov_idx] = (struct kvec) { work->tr_buf, + sizeof(struct smb2_transform_hdr) + 4 }; + len += iov[iov_idx++].iov_len; + } + + if (work->aux_payload_sz) { + iov[iov_idx] = (struct kvec) { work->response_buf, work->resp_hdr_sz }; + len += iov[iov_idx++].iov_len; + iov[iov_idx] = (struct kvec) { work->aux_payload_buf, work->aux_payload_sz }; + len += iov[iov_idx++].iov_len; + } else { + if (work->tr_buf) + iov[iov_idx].iov_len = work->resp_hdr_sz; + else + iov[iov_idx].iov_len = get_rfc1002_len(work->response_buf) + 4; + iov[iov_idx].iov_base = work->response_buf; + len += iov[iov_idx++].iov_len; + } + + ksmbd_conn_lock(conn); + sent = conn->transport->ops->writev(conn->transport, &iov[0], + iov_idx, len, + work->need_invalidate_rkey, + work->remote_key); + ksmbd_conn_unlock(conn); + + if (sent < 0) { + pr_err("Failed to send message: %d\n", sent); + return sent; + } + + return 0; +} + +int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, + void *buf, unsigned int buflen, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len) +{ + int ret = -EINVAL; + + if (conn->transport->ops->rdma_read) + ret = conn->transport->ops->rdma_read(conn->transport, + buf, buflen, + desc, desc_len); + return ret; +} + +int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, + void *buf, unsigned int buflen, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len) +{ + int ret = -EINVAL; + + if (conn->transport->ops->rdma_write) + ret = conn->transport->ops->rdma_write(conn->transport, + buf, buflen, + desc, desc_len); + return ret; +} + +bool ksmbd_conn_alive(struct ksmbd_conn *conn) +{ + if (!ksmbd_server_running()) + return false; + + if (conn->status == KSMBD_SESS_EXITING) + return false; + + if (kthread_should_stop()) + return false; + + if (atomic_read(&conn->stats.open_files_count) > 0) + return true; + + /* + * Stop current session if the time that get last request from client + * is bigger than deadtime user configured and opening file count is + * zero. + */ + if (server_conf.deadtime > 0 && + time_after(jiffies, conn->last_active + server_conf.deadtime)) { + ksmbd_debug(CONN, "No response from client in %lu minutes\n", + server_conf.deadtime / SMB_ECHO_INTERVAL); + return false; + } + return true; +} + +/** + * ksmbd_conn_handler_loop() - session thread to listen on new smb requests + * @p: connection instance + * + * One thread each per connection + * + * Return: 0 on success + */ +int ksmbd_conn_handler_loop(void *p) +{ + struct ksmbd_conn *conn = (struct ksmbd_conn *)p; + struct ksmbd_transport *t = conn->transport; + unsigned int pdu_size, max_allowed_pdu_size; + char hdr_buf[4] = {0,}; + int size; + + mutex_init(&conn->srv_mutex); + __module_get(THIS_MODULE); + + if (t->ops->prepare && t->ops->prepare(t)) + goto out; + + conn->last_active = jiffies; + while (ksmbd_conn_alive(conn)) { + if (try_to_freeze()) + continue; + + kvfree(conn->request_buf); + conn->request_buf = NULL; + + size = t->ops->read(t, hdr_buf, sizeof(hdr_buf)); + if (size != sizeof(hdr_buf)) + break; + + pdu_size = get_rfc1002_len(hdr_buf); + ksmbd_debug(CONN, "RFC1002 header %u bytes\n", pdu_size); + + /* make sure we have enough to get to SMB header end */ + if (!ksmbd_pdu_size_has_room(pdu_size)) { + ksmbd_debug(CONN, "SMB request too short (%u bytes)\n", + pdu_size); + break; + } + + if (conn->status == KSMBD_SESS_GOOD) + max_allowed_pdu_size = + SMB3_MAX_MSGSIZE + conn->vals->max_write_size; + else + max_allowed_pdu_size = SMB3_MAX_MSGSIZE; + + if (pdu_size > max_allowed_pdu_size) { + pr_err_ratelimited("PDU length(%u) excceed maximum allowed pdu size(%u) on connection(%d)\n", + pdu_size, max_allowed_pdu_size, + conn->status); + break; + } + + if (pdu_size > MAX_STREAM_PROT_LEN) + break; + + /* 4 for rfc1002 length field */ + size = pdu_size + 4; + conn->request_buf = kvmalloc(size, + GFP_KERNEL | + __GFP_NOWARN | + __GFP_NORETRY); + if (!conn->request_buf) + break; + + memcpy(conn->request_buf, hdr_buf, sizeof(hdr_buf)); + if (!ksmbd_smb_request(conn)) + break; + + /* + * We already read 4 bytes to find out PDU size, now + * read in PDU + */ + size = t->ops->read(t, conn->request_buf + 4, pdu_size); + if (size < 0) { + pr_err("sock_read failed: %d\n", size); + break; + } + + if (size != pdu_size) { + pr_err("PDU error. Read: %d, Expected: %d\n", + size, pdu_size); + continue; + } + + if (!default_conn_ops.process_fn) { + pr_err("No connection request callback\n"); + break; + } + + if (default_conn_ops.process_fn(conn)) { + pr_err("Cannot handle request\n"); + break; + } + } + +out: + /* Wait till all reference dropped to the Server object*/ + wait_event(conn->r_count_q, atomic_read(&conn->r_count) == 0); + + + if (IS_ENABLED(CONFIG_UNICODE)) + utf8_unload(conn->um); + unload_nls(conn->local_nls); + if (default_conn_ops.terminate_fn) + default_conn_ops.terminate_fn(conn); + t->ops->disconnect(t); + module_put(THIS_MODULE); + return 0; +} + +void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops) +{ + default_conn_ops.process_fn = ops->process_fn; + default_conn_ops.terminate_fn = ops->terminate_fn; +} + +int ksmbd_conn_transport_init(void) +{ + int ret; + + mutex_lock(&init_lock); + ret = ksmbd_tcp_init(); + if (ret) { + pr_err("Failed to init TCP subsystem: %d\n", ret); + goto out; + } + + ret = ksmbd_rdma_init(); + if (ret) { + pr_err("Failed to init RDMA subsystem: %d\n", ret); + goto out; + } +out: + mutex_unlock(&init_lock); + return ret; +} + +static void stop_sessions(void) +{ + struct ksmbd_conn *conn; + struct ksmbd_transport *t; + +again: + read_lock(&conn_list_lock); + list_for_each_entry(conn, &conn_list, conns_list) { + struct task_struct *task; + + t = conn->transport; + task = t->handler; + if (task) + ksmbd_debug(CONN, "Stop session handler %s/%d\n", + task->comm, task_pid_nr(task)); + conn->status = KSMBD_SESS_EXITING; + if (t->ops->shutdown) { + read_unlock(&conn_list_lock); + t->ops->shutdown(t); + read_lock(&conn_list_lock); + } + } + read_unlock(&conn_list_lock); + + if (!list_empty(&conn_list)) { + schedule_timeout_interruptible(HZ / 10); /* 100ms */ + goto again; + } +} + +void ksmbd_conn_transport_destroy(void) +{ + mutex_lock(&init_lock); + ksmbd_tcp_destroy(); + ksmbd_rdma_destroy(); + stop_sessions(); + mutex_unlock(&init_lock); +} diff --git a/fs/ksmbd/connection.h b/fs/ksmbd/connection.h new file mode 100644 index 0000000000000..3643354a3fa79 --- /dev/null +++ b/fs/ksmbd/connection.h @@ -0,0 +1,210 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_CONNECTION_H__ +#define __KSMBD_CONNECTION_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "smb_common.h" +#include "ksmbd_work.h" + +#define KSMBD_SOCKET_BACKLOG 16 + +enum { + KSMBD_SESS_NEW = 0, + KSMBD_SESS_GOOD, + KSMBD_SESS_EXITING, + KSMBD_SESS_NEED_RECONNECT, + KSMBD_SESS_NEED_NEGOTIATE +}; + +struct ksmbd_stats { + atomic_t open_files_count; + atomic64_t request_served; +}; + +struct ksmbd_transport; + +struct ksmbd_conn { + struct smb_version_values *vals; + struct smb_version_ops *ops; + struct smb_version_cmds *cmds; + unsigned int max_cmds; + struct mutex srv_mutex; + int status; + unsigned int cli_cap; + char *request_buf; + struct ksmbd_transport *transport; + struct nls_table *local_nls; + struct unicode_map *um; + struct list_head conns_list; + /* smb session 1 per user */ + struct xarray sessions; + unsigned long last_active; + /* How many request are running currently */ + atomic_t req_running; + /* References which are made for this Server object*/ + atomic_t r_count; + unsigned int total_credits; + unsigned int outstanding_credits; + spinlock_t credits_lock; + wait_queue_head_t req_running_q; + wait_queue_head_t r_count_q; + /* Lock to protect requests list*/ + spinlock_t request_lock; + struct list_head requests; + struct list_head async_requests; + int connection_type; + struct ksmbd_stats stats; + char ClientGUID[SMB2_CLIENT_GUID_SIZE]; + struct ntlmssp_auth ntlmssp; + + spinlock_t llist_lock; + struct list_head lock_list; + + struct preauth_integrity_info *preauth_info; + + bool need_neg; + unsigned int auth_mechs; + unsigned int preferred_auth_mech; + bool sign; + bool use_spnego:1; + __u16 cli_sec_mode; + __u16 srv_sec_mode; + /* dialect index that server chose */ + __u16 dialect; + + char *mechToken; + + struct ksmbd_conn_ops *conn_ops; + + /* Preauth Session Table */ + struct list_head preauth_sess_table; + + struct sockaddr_storage peer_addr; + + /* Identifier for async message */ + struct ida async_ida; + + __le16 cipher_type; + __le16 compress_algorithm; + bool posix_ext_supported; + bool signing_negotiated; + __le16 signing_algorithm; + bool binding; +}; + +struct ksmbd_conn_ops { + int (*process_fn)(struct ksmbd_conn *conn); + int (*terminate_fn)(struct ksmbd_conn *conn); +}; + +struct ksmbd_transport_ops { + int (*prepare)(struct ksmbd_transport *t); + void (*disconnect)(struct ksmbd_transport *t); + void (*shutdown)(struct ksmbd_transport *t); + int (*read)(struct ksmbd_transport *t, char *buf, unsigned int size); + int (*writev)(struct ksmbd_transport *t, struct kvec *iovs, int niov, + int size, bool need_invalidate_rkey, + unsigned int remote_key); + int (*rdma_read)(struct ksmbd_transport *t, + void *buf, unsigned int len, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len); + int (*rdma_write)(struct ksmbd_transport *t, + void *buf, unsigned int len, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len); +}; + +struct ksmbd_transport { + struct ksmbd_conn *conn; + struct ksmbd_transport_ops *ops; + struct task_struct *handler; +}; + +#define KSMBD_TCP_RECV_TIMEOUT (7 * HZ) +#define KSMBD_TCP_SEND_TIMEOUT (5 * HZ) +#define KSMBD_TCP_PEER_SOCKADDR(c) ((struct sockaddr *)&((c)->peer_addr)) + +extern struct list_head conn_list; +extern rwlock_t conn_list_lock; + +bool ksmbd_conn_alive(struct ksmbd_conn *conn); +void ksmbd_conn_wait_idle(struct ksmbd_conn *conn); +struct ksmbd_conn *ksmbd_conn_alloc(void); +void ksmbd_conn_free(struct ksmbd_conn *conn); +bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c); +int ksmbd_conn_write(struct ksmbd_work *work); +int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, + void *buf, unsigned int buflen, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len); +int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, + void *buf, unsigned int buflen, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len); +void ksmbd_conn_enqueue_request(struct ksmbd_work *work); +int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work); +void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops); +int ksmbd_conn_handler_loop(void *p); +int ksmbd_conn_transport_init(void); +void ksmbd_conn_transport_destroy(void); + +/* + * WARNING + * + * This is a hack. We will move status to a proper place once we land + * a multi-sessions support. + */ +static inline bool ksmbd_conn_good(struct ksmbd_work *work) +{ + return work->conn->status == KSMBD_SESS_GOOD; +} + +static inline bool ksmbd_conn_need_negotiate(struct ksmbd_work *work) +{ + return work->conn->status == KSMBD_SESS_NEED_NEGOTIATE; +} + +static inline bool ksmbd_conn_need_reconnect(struct ksmbd_work *work) +{ + return work->conn->status == KSMBD_SESS_NEED_RECONNECT; +} + +static inline bool ksmbd_conn_exiting(struct ksmbd_work *work) +{ + return work->conn->status == KSMBD_SESS_EXITING; +} + +static inline void ksmbd_conn_set_good(struct ksmbd_work *work) +{ + work->conn->status = KSMBD_SESS_GOOD; +} + +static inline void ksmbd_conn_set_need_negotiate(struct ksmbd_work *work) +{ + work->conn->status = KSMBD_SESS_NEED_NEGOTIATE; +} + +static inline void ksmbd_conn_set_need_reconnect(struct ksmbd_work *work) +{ + work->conn->status = KSMBD_SESS_NEED_RECONNECT; +} + +static inline void ksmbd_conn_set_exiting(struct ksmbd_work *work) +{ + work->conn->status = KSMBD_SESS_EXITING; +} +#endif /* __CONNECTION_H__ */ diff --git a/fs/ksmbd/crypto_ctx.c b/fs/ksmbd/crypto_ctx.c new file mode 100644 index 0000000000000..5f4b1008d17e0 --- /dev/null +++ b/fs/ksmbd/crypto_ctx.c @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include + +#include "glob.h" +#include "crypto_ctx.h" + +struct crypto_ctx_list { + spinlock_t ctx_lock; + int avail_ctx; + struct list_head idle_ctx; + wait_queue_head_t ctx_wait; +}; + +static struct crypto_ctx_list ctx_list; + +static inline void free_aead(struct crypto_aead *aead) +{ + if (aead) + crypto_free_aead(aead); +} + +static void free_shash(struct shash_desc *shash) +{ + if (shash) { + crypto_free_shash(shash->tfm); + kfree(shash); + } +} + +static struct crypto_aead *alloc_aead(int id) +{ + struct crypto_aead *tfm = NULL; + + switch (id) { + case CRYPTO_AEAD_AES_GCM: + tfm = crypto_alloc_aead("gcm(aes)", 0, 0); + break; + case CRYPTO_AEAD_AES_CCM: + tfm = crypto_alloc_aead("ccm(aes)", 0, 0); + break; + default: + pr_err("Does not support encrypt ahead(id : %d)\n", id); + return NULL; + } + + if (IS_ERR(tfm)) { + pr_err("Failed to alloc encrypt aead : %ld\n", PTR_ERR(tfm)); + return NULL; + } + + return tfm; +} + +static struct shash_desc *alloc_shash_desc(int id) +{ + struct crypto_shash *tfm = NULL; + struct shash_desc *shash; + + switch (id) { + case CRYPTO_SHASH_HMACMD5: + tfm = crypto_alloc_shash("hmac(md5)", 0, 0); + break; + case CRYPTO_SHASH_HMACSHA256: + tfm = crypto_alloc_shash("hmac(sha256)", 0, 0); + break; + case CRYPTO_SHASH_CMACAES: + tfm = crypto_alloc_shash("cmac(aes)", 0, 0); + break; + case CRYPTO_SHASH_SHA256: + tfm = crypto_alloc_shash("sha256", 0, 0); + break; + case CRYPTO_SHASH_SHA512: + tfm = crypto_alloc_shash("sha512", 0, 0); + break; + case CRYPTO_SHASH_MD4: + tfm = crypto_alloc_shash("md4", 0, 0); + break; + case CRYPTO_SHASH_MD5: + tfm = crypto_alloc_shash("md5", 0, 0); + break; + default: + return NULL; + } + + if (IS_ERR(tfm)) + return NULL; + + shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm), + GFP_KERNEL); + if (!shash) + crypto_free_shash(tfm); + else + shash->tfm = tfm; + return shash; +} + +static void ctx_free(struct ksmbd_crypto_ctx *ctx) +{ + int i; + + for (i = 0; i < CRYPTO_SHASH_MAX; i++) + free_shash(ctx->desc[i]); + for (i = 0; i < CRYPTO_AEAD_MAX; i++) + free_aead(ctx->ccmaes[i]); + kfree(ctx); +} + +static struct ksmbd_crypto_ctx *ksmbd_find_crypto_ctx(void) +{ + struct ksmbd_crypto_ctx *ctx; + + while (1) { + spin_lock(&ctx_list.ctx_lock); + if (!list_empty(&ctx_list.idle_ctx)) { + ctx = list_entry(ctx_list.idle_ctx.next, + struct ksmbd_crypto_ctx, + list); + list_del(&ctx->list); + spin_unlock(&ctx_list.ctx_lock); + return ctx; + } + + if (ctx_list.avail_ctx > num_online_cpus()) { + spin_unlock(&ctx_list.ctx_lock); + wait_event(ctx_list.ctx_wait, + !list_empty(&ctx_list.idle_ctx)); + continue; + } + + ctx_list.avail_ctx++; + spin_unlock(&ctx_list.ctx_lock); + + ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), GFP_KERNEL); + if (!ctx) { + spin_lock(&ctx_list.ctx_lock); + ctx_list.avail_ctx--; + spin_unlock(&ctx_list.ctx_lock); + wait_event(ctx_list.ctx_wait, + !list_empty(&ctx_list.idle_ctx)); + continue; + } + break; + } + return ctx; +} + +void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx) +{ + if (!ctx) + return; + + spin_lock(&ctx_list.ctx_lock); + if (ctx_list.avail_ctx <= num_online_cpus()) { + list_add(&ctx->list, &ctx_list.idle_ctx); + spin_unlock(&ctx_list.ctx_lock); + wake_up(&ctx_list.ctx_wait); + return; + } + + ctx_list.avail_ctx--; + spin_unlock(&ctx_list.ctx_lock); + ctx_free(ctx); +} + +static struct ksmbd_crypto_ctx *____crypto_shash_ctx_find(int id) +{ + struct ksmbd_crypto_ctx *ctx; + + if (id >= CRYPTO_SHASH_MAX) + return NULL; + + ctx = ksmbd_find_crypto_ctx(); + if (ctx->desc[id]) + return ctx; + + ctx->desc[id] = alloc_shash_desc(id); + if (ctx->desc[id]) + return ctx; + ksmbd_release_crypto_ctx(ctx); + return NULL; +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACMD5); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACSHA256); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_CMACAES); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha256(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA256); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA512); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md4(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_MD4); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md5(void) +{ + return ____crypto_shash_ctx_find(CRYPTO_SHASH_MD5); +} + +static struct ksmbd_crypto_ctx *____crypto_aead_ctx_find(int id) +{ + struct ksmbd_crypto_ctx *ctx; + + if (id >= CRYPTO_AEAD_MAX) + return NULL; + + ctx = ksmbd_find_crypto_ctx(); + if (ctx->ccmaes[id]) + return ctx; + + ctx->ccmaes[id] = alloc_aead(id); + if (ctx->ccmaes[id]) + return ctx; + ksmbd_release_crypto_ctx(ctx); + return NULL; +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void) +{ + return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES_GCM); +} + +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void) +{ + return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES_CCM); +} + +void ksmbd_crypto_destroy(void) +{ + struct ksmbd_crypto_ctx *ctx; + + while (!list_empty(&ctx_list.idle_ctx)) { + ctx = list_entry(ctx_list.idle_ctx.next, + struct ksmbd_crypto_ctx, + list); + list_del(&ctx->list); + ctx_free(ctx); + } +} + +int ksmbd_crypto_create(void) +{ + struct ksmbd_crypto_ctx *ctx; + + spin_lock_init(&ctx_list.ctx_lock); + INIT_LIST_HEAD(&ctx_list.idle_ctx); + init_waitqueue_head(&ctx_list.ctx_wait); + ctx_list.avail_ctx = 1; + + ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + list_add(&ctx->list, &ctx_list.idle_ctx); + return 0; +} diff --git a/fs/ksmbd/crypto_ctx.h b/fs/ksmbd/crypto_ctx.h new file mode 100644 index 0000000000000..ef11154b43df3 --- /dev/null +++ b/fs/ksmbd/crypto_ctx.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#ifndef __CRYPTO_CTX_H__ +#define __CRYPTO_CTX_H__ + +#include +#include + +enum { + CRYPTO_SHASH_HMACMD5 = 0, + CRYPTO_SHASH_HMACSHA256, + CRYPTO_SHASH_CMACAES, + CRYPTO_SHASH_SHA256, + CRYPTO_SHASH_SHA512, + CRYPTO_SHASH_MD4, + CRYPTO_SHASH_MD5, + CRYPTO_SHASH_MAX, +}; + +enum { + CRYPTO_AEAD_AES_GCM = 16, + CRYPTO_AEAD_AES_CCM, + CRYPTO_AEAD_MAX, +}; + +enum { + CRYPTO_BLK_ECBDES = 32, + CRYPTO_BLK_MAX, +}; + +struct ksmbd_crypto_ctx { + struct list_head list; + + struct shash_desc *desc[CRYPTO_SHASH_MAX]; + struct crypto_aead *ccmaes[CRYPTO_AEAD_MAX]; +}; + +#define CRYPTO_HMACMD5(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]) +#define CRYPTO_HMACSHA256(c) ((c)->desc[CRYPTO_SHASH_HMACSHA256]) +#define CRYPTO_CMACAES(c) ((c)->desc[CRYPTO_SHASH_CMACAES]) +#define CRYPTO_SHA256(c) ((c)->desc[CRYPTO_SHASH_SHA256]) +#define CRYPTO_SHA512(c) ((c)->desc[CRYPTO_SHASH_SHA512]) +#define CRYPTO_MD4(c) ((c)->desc[CRYPTO_SHASH_MD4]) +#define CRYPTO_MD5(c) ((c)->desc[CRYPTO_SHASH_MD5]) + +#define CRYPTO_HMACMD5_TFM(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]->tfm) +#define CRYPTO_HMACSHA256_TFM(c)\ + ((c)->desc[CRYPTO_SHASH_HMACSHA256]->tfm) +#define CRYPTO_CMACAES_TFM(c) ((c)->desc[CRYPTO_SHASH_CMACAES]->tfm) +#define CRYPTO_SHA256_TFM(c) ((c)->desc[CRYPTO_SHASH_SHA256]->tfm) +#define CRYPTO_SHA512_TFM(c) ((c)->desc[CRYPTO_SHASH_SHA512]->tfm) +#define CRYPTO_MD4_TFM(c) ((c)->desc[CRYPTO_SHASH_MD4]->tfm) +#define CRYPTO_MD5_TFM(c) ((c)->desc[CRYPTO_SHASH_MD5]->tfm) + +#define CRYPTO_GCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_GCM]) +#define CRYPTO_CCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_CCM]) + +void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha256(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md4(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md5(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void); +struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void); +void ksmbd_crypto_destroy(void); +int ksmbd_crypto_create(void); + +#endif /* __CRYPTO_CTX_H__ */ diff --git a/fs/ksmbd/dkms.conf b/fs/ksmbd/dkms.conf new file mode 100644 index 0000000000000..61f057559ead2 --- /dev/null +++ b/fs/ksmbd/dkms.conf @@ -0,0 +1,6 @@ +PACKAGE_NAME="ksmbd" +PACKAGE_VERSION="@VERSION@" +MAKE="'make' KDIR=$kernel_source_dir" +BUILT_MODULE_NAME="ksmbd" +DEST_MODULE_LOCATION="/kernel/fs/ksmbd" +AUTOINSTALL="yes" diff --git a/fs/ksmbd/glob.h b/fs/ksmbd/glob.h new file mode 100644 index 0000000000000..41e920fdcb932 --- /dev/null +++ b/fs/ksmbd/glob.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_GLOB_H +#define __KSMBD_GLOB_H + +#include + +#include "unicode.h" +#include "vfs_cache.h" + +#define KSMBD_VERSION "3.4.7" + +extern int ksmbd_debug_types; + +#define KSMBD_DEBUG_SMB BIT(0) +#define KSMBD_DEBUG_AUTH BIT(1) +#define KSMBD_DEBUG_VFS BIT(2) +#define KSMBD_DEBUG_OPLOCK BIT(3) +#define KSMBD_DEBUG_IPC BIT(4) +#define KSMBD_DEBUG_CONN BIT(5) +#define KSMBD_DEBUG_RDMA BIT(6) +#define KSMBD_DEBUG_ALL (KSMBD_DEBUG_SMB | KSMBD_DEBUG_AUTH | \ + KSMBD_DEBUG_VFS | KSMBD_DEBUG_OPLOCK | \ + KSMBD_DEBUG_IPC | KSMBD_DEBUG_CONN | \ + KSMBD_DEBUG_RDMA) + +#ifdef pr_fmt +#undef pr_fmt +#endif + +#ifdef SUBMOD_NAME +#define pr_fmt(fmt) "ksmbd: " SUBMOD_NAME ": " fmt +#else +#define pr_fmt(fmt) "ksmbd: " fmt +#endif + +#define ksmbd_debug(type, fmt, ...) \ + do { \ + if (ksmbd_debug_types & KSMBD_DEBUG_##type) \ + pr_info(fmt, ##__VA_ARGS__); \ + } while (0) + +#define UNICODE_LEN(x) ((x) * 2) + +#ifdef CONFIG_SMB_INSECURE_SERVER +/* ksmbd misc functions */ +extern void ntstatus_to_dos(__le32 ntstatus, __u8 *eclass, __le16 *ecode); +#endif + +#ifndef LOOKUP_NO_SYMLINKS +#define LOOKUP_NO_SYMLINKS 0 +#endif + +#endif /* __KSMBD_GLOB_H */ diff --git a/fs/ksmbd/ksmbd.rst b/fs/ksmbd/ksmbd.rst new file mode 100644 index 0000000000000..438eca2ed2785 --- /dev/null +++ b/fs/ksmbd/ksmbd.rst @@ -0,0 +1,183 @@ +.. SPDX-License-Identifier: GPL-2.0 + +========================== +KSMBD - SMB3 Kernel Server +========================== + +ksmbd is a linux kernel server which implements SMB3 protocol in kernel space +for sharing files over network. + +KSMBD architecture +================== + +The subset of performance related operations belong in kernelspace and +the other subset which belong to operations which are not really related with +performance in userspace. So, DCE/RPC management that has historically resulted +into number of buffer overflow issues and dangerous security bugs and user +account management are implemented in user space as ksmbd.mountd. +File operations that are related with performance (open/read/write/close etc.) +in kernel space (ksmbd). This also allows for easier integration with VFS +interface for all file operations. + +ksmbd (kernel daemon) +--------------------- + +When the server daemon is started, It starts up a forker thread +(ksmbd/interface name) at initialization time and open a dedicated port 445 +for listening to SMB requests. Whenever new clients make request, Forker +thread will accept the client connection and fork a new thread for dedicated +communication channel between the client and the server. It allows for parallel +processing of SMB requests(commands) from clients as well as allowing for new +clients to make new connections. Each instance is named ksmbd/1~n(port number) +to indicate connected clients. Depending on the SMB request types, each new +thread can decide to pass through the commands to the user space (ksmbd.mountd), +currently DCE/RPC commands are identified to be handled through the user space. +To further utilize the linux kernel, it has been chosen to process the commands +as workitems and to be executed in the handlers of the ksmbd-io kworker threads. +It allows for multiplexing of the handlers as the kernel take care of initiating +extra worker threads if the load is increased and vice versa, if the load is +decreased it destroys the extra worker threads. So, after connection is +established with client. Dedicated ksmbd/1..n(port number) takes complete +ownership of receiving/parsing of SMB commands. Each received command is worked +in parallel i.e., There can be multiple clients commands which are worked in +parallel. After receiving each command a separated kernel workitem is prepared +for each command which is further queued to be handled by ksmbd-io kworkers. +So, each SMB workitem is queued to the kworkers. This allows the benefit of load +sharing to be managed optimally by the default kernel and optimizing client +performance by handling client commands in parallel. + +ksmbd.mountd (user space daemon) +-------------------------------- + +ksmbd.mountd is userspace process to, transfer user account and password that +are registered using ksmbd.adduser(part of utils for user space). Further it +allows sharing information parameters that parsed from smb.conf to ksmbd in +kernel. For the execution part it has a daemon which is continuously running +and connected to the kernel interface using netlink socket, it waits for the +requests(dcerpc and share/user info). It handles RPC calls (at a minimum few +dozen) that are most important for file server from NetShareEnum and +NetServerGetInfo. Complete DCE/RPC response is prepared from the user space +and passed over to the associated kernel thread for the client. + + +KSMBD Feature Status +==================== + +============================== ================================================= +Feature name Status +============================== ================================================= +Dialects Supported. SMB2.1 SMB3.0, SMB3.1.1 dialects + (intentionally excludes security vulnerable SMB1 + dialect). +Auto Negotiation Supported. +Compound Request Supported. +Oplock Cache Mechanism Supported. +SMB2 leases(v1 lease) Supported. +Directory leases(v2 lease) Planned for future. +Multi-credits Supported. +NTLM/NTLMv2 Supported. +HMAC-SHA256 Signing Supported. +Secure negotiate Supported. +Signing Update Supported. +Pre-authentication integrity Supported. +SMB3 encryption(CCM, GCM) Supported. (CCM and GCM128 supported, GCM256 in + progress) +SMB direct(RDMA) Partially Supported. SMB3 Multi-channel is + required to connect to Windows client. +SMB3 Multi-channel Partially Supported. Planned to implement + replay/retry mechanisms for future. +SMB3.1.1 POSIX extension Supported. +ACLs Partially Supported. only DACLs available, SACLs + (auditing) is planned for the future. For + ownership (SIDs) ksmbd generates random subauth + values(then store it to disk) and use uid/gid + get from inode as RID for local domain SID. + The current acl implementation is limited to + standalone server, not a domain member. + Integration with Samba tools is being worked on + to allow future support for running as a domain + member. +Kerberos Supported. +Durable handle v1,v2 Planned for future. +Persistent handle Planned for future. +SMB2 notify Planned for future. +Sparse file support Supported. +DCE/RPC support Partially Supported. a few calls(NetShareEnumAll, + NetServerGetInfo, SAMR, LSARPC) that are needed + for file server handled via netlink interface + from ksmbd.mountd. Additional integration with + Samba tools and libraries via upcall is being + investigated to allow support for additional + DCE/RPC management calls (and future support + for Witness protocol e.g.) +ksmbd/nfsd interoperability Planned for future. The features that ksmbd + support are Leases, Notify, ACLs and Share modes. +============================== ================================================= + + +How to run +========== + +1. Download ksmbd-tools(https://github.com/cifsd-team/ksmbd-tools/releases) and + compile them. + + - Refer README(https://github.com/cifsd-team/ksmbd-tools/blob/master/README.md) + to know how to use ksmbd.mountd/adduser/addshare/control utils + + $ ./autogen.sh + $ ./configure --with-rundir=/run + $ make && sudo make install + +2. Create /usr/local/etc/ksmbd/ksmbd.conf file, add SMB share in ksmbd.conf file. + + - Refer ksmbd.conf.example in ksmbd-utils, See ksmbd.conf manpage + for details to configure shares. + + $ man ksmbd.conf + +3. Create user/password for SMB share. + + - See ksmbd.adduser manpage. + + $ man ksmbd.adduser + $ sudo ksmbd.adduser -a + +4. Insert ksmbd.ko module after build your kernel. No need to load module + if ksmbd is built into the kernel. + + - Set ksmbd in menuconfig(e.g. $ make menuconfig) + [*] Network File Systems ---> + SMB3 server support (EXPERIMENTAL) + + $ sudo modprobe ksmbd.ko + +5. Start ksmbd user space daemon + + $ sudo ksmbd.mountd + +6. Access share from Windows or Linux using SMB3 client (cifs.ko or smbclient of samba) + +Shutdown KSMBD +============== + +1. kill user and kernel space daemon + # sudo ksmbd.control -s + +How to turn debug print on +========================== + +Each layer +/sys/class/ksmbd-control/debug + +1. Enable all component prints + # sudo ksmbd.control -d "all" + +2. Enable one of components(smb, auth, vfs, oplock, ipc, conn, rdma) + # sudo ksmbd.control -d "smb" + +3. Show what prints are enable. + # cat/sys/class/ksmbd-control/debug + [smb] auth vfs oplock ipc conn [rdma] + +4. Disable prints: + If you try the selected component once more, It is disabled without brackets. diff --git a/fs/ksmbd/ksmbd_netlink.h b/fs/ksmbd/ksmbd_netlink.h new file mode 100644 index 0000000000000..fb9626383f865 --- /dev/null +++ b/fs/ksmbd/ksmbd_netlink.h @@ -0,0 +1,413 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + * + * linux-ksmbd-devel@lists.sourceforge.net + */ + +#ifndef _LINUX_KSMBD_SERVER_H +#define _LINUX_KSMBD_SERVER_H + +#include + +/* + * This is a userspace ABI to communicate data between ksmbd and user IPC + * daemon using netlink. This is added to track and cache user account DB + * and share configuration info from userspace. + * + * - KSMBD_EVENT_HEARTBEAT_REQUEST(ksmbd_heartbeat) + * This event is to check whether user IPC daemon is alive. If user IPC + * daemon is dead, ksmbd keep existing connection till disconnecting and + * new connection will be denied. + * + * - KSMBD_EVENT_STARTING_UP(ksmbd_startup_request) + * This event is to receive the information that initializes the ksmbd + * server from the user IPC daemon and to start the server. The global + * section parameters are given from smb.conf as initialization + * information. + * + * - KSMBD_EVENT_SHUTTING_DOWN(ksmbd_shutdown_request) + * This event is to shutdown ksmbd server. + * + * - KSMBD_EVENT_LOGIN_REQUEST/RESPONSE(ksmbd_login_request/response) + * This event is to get user account info to user IPC daemon. + * + * - KSMBD_EVENT_SHARE_CONFIG_REQUEST/RESPONSE(ksmbd_share_config_request/response) + * This event is to get net share configuration info. + * + * - KSMBD_EVENT_TREE_CONNECT_REQUEST/RESPONSE(ksmbd_tree_connect_request/response) + * This event is to get session and tree connect info. + * + * - KSMBD_EVENT_TREE_DISCONNECT_REQUEST(ksmbd_tree_disconnect_request) + * This event is to send tree disconnect info to user IPC daemon. + * + * - KSMBD_EVENT_LOGOUT_REQUEST(ksmbd_logout_request) + * This event is to send logout request to user IPC daemon. + * + * - KSMBD_EVENT_RPC_REQUEST/RESPONSE(ksmbd_rpc_command) + * This event is to make DCE/RPC request like srvsvc, wkssvc, lsarpc, + * samr to be processed in userspace. + * + * - KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST/RESPONSE(ksmbd_spnego_authen_request/response) + * This event is to make kerberos authentication to be processed in + * userspace. + */ + +#define KSMBD_GENL_NAME "SMBD_GENL" +#define KSMBD_GENL_VERSION 0x01 + +#define KSMBD_REQ_MAX_ACCOUNT_NAME_SZ 48 +#define KSMBD_REQ_MAX_HASH_SZ 18 +#define KSMBD_REQ_MAX_SHARE_NAME 64 + +/* + * IPC heartbeat frame to check whether user IPC daemon is alive. + */ +struct ksmbd_heartbeat { + __u32 handle; +}; + +/* + * Global config flags. + */ +#define KSMBD_GLOBAL_FLAG_INVALID (0) +#define KSMBD_GLOBAL_FLAG_SMB2_LEASES BIT(0) +#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION BIT(1) +#define KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL BIT(2) +#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF BIT(3) + +/* + * IPC request for ksmbd server startup + */ +struct ksmbd_startup_request { + __u32 flags; /* Flags for global config */ + __s32 signing; /* Signing enabled */ + __s8 min_prot[16]; /* The minimum SMB protocol version */ + __s8 max_prot[16]; /* The maximum SMB protocol version */ + __s8 netbios_name[16]; + __s8 work_group[64]; /* Workgroup */ + __s8 server_string[64]; /* Server string */ + __u16 tcp_port; /* tcp port */ + __u16 ipc_timeout; /* + * specifies the number of seconds + * server will wait for the userspace to + * reply to heartbeat frames. + */ + __u32 deadtime; /* Number of minutes of inactivity */ + __u32 file_max; /* Limits the maximum number of open files */ + __u32 smb2_max_write; /* MAX write size */ + __u32 smb2_max_read; /* MAX read size */ + __u32 smb2_max_trans; /* MAX trans size */ + __u32 share_fake_fscaps; /* + * Support some special application that + * makes QFSINFO calls to check whether + * we set the SPARSE_FILES bit (0x40). + */ + __u32 sub_auth[3]; /* Subauth value for Security ID */ + __u32 smb2_max_credits; /* MAX credits */ + __u32 smbd_max_io_size; /* smbd read write size */ + __u32 max_connections; /* Number of maximum simultaneous connections */ + __u32 reserved[126]; /* Reserved room */ + __u32 ifc_list_sz; /* interfaces list size */ + __s8 ____payload[]; +}; + +#define KSMBD_STARTUP_CONFIG_INTERFACES(s) ((s)->____payload) + +/* + * IPC request to shutdown ksmbd server. + */ +struct ksmbd_shutdown_request { + __s32 reserved[16]; +}; + +/* + * IPC user login request. + */ +struct ksmbd_login_request { + __u32 handle; + __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */ + __u32 reserved[16]; /* Reserved room */ +}; + +/* + * IPC user login response. + */ +struct ksmbd_login_response { + __u32 handle; + __u32 gid; /* group id */ + __u32 uid; /* user id */ + __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */ + __u16 status; + __u16 hash_sz; /* hash size */ + __s8 hash[KSMBD_REQ_MAX_HASH_SZ]; /* password hash */ + __u32 reserved[16]; /* Reserved room */ +}; + +/* + * IPC request to fetch net share config. + */ +struct ksmbd_share_config_request { + __u32 handle; + __s8 share_name[KSMBD_REQ_MAX_SHARE_NAME]; /* share name */ + __u32 reserved[16]; /* Reserved room */ +}; + +/* + * IPC response to the net share config request. + */ +struct ksmbd_share_config_response { + __u32 handle; + __u32 flags; + __u16 create_mask; + __u16 directory_mask; + __u16 force_create_mode; + __u16 force_directory_mode; + __u16 force_uid; + __u16 force_gid; + __s8 share_name[KSMBD_REQ_MAX_SHARE_NAME]; + __u32 reserved[112]; /* Reserved room */ + __u32 veto_list_sz; + __s8 ____payload[]; +}; + +#define KSMBD_SHARE_CONFIG_VETO_LIST(s) ((s)->____payload) + +static inline char * +ksmbd_share_config_path(struct ksmbd_share_config_response *sc) +{ + char *p = sc->____payload; + + if (sc->veto_list_sz) + p += sc->veto_list_sz + 1; + + return p; +} + +/* + * IPC request for tree connection. This request include session and tree + * connect info from client. + */ +struct ksmbd_tree_connect_request { + __u32 handle; + __u16 account_flags; + __u16 flags; + __u64 session_id; + __u64 connect_id; + __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; + __s8 share[KSMBD_REQ_MAX_SHARE_NAME]; + __s8 peer_addr[64]; + __u32 reserved[16]; /* Reserved room */ +}; + +/* + * IPC Response structure for tree connection. + */ +struct ksmbd_tree_connect_response { + __u32 handle; + __u16 status; + __u16 connection_flags; + __u32 reserved[16]; /* Reserved room */ +}; + +/* + * IPC Request struture to disconnect tree connection. + */ +struct ksmbd_tree_disconnect_request { + __u64 session_id; /* session id */ + __u64 connect_id; /* tree connection id */ + __u32 reserved[16]; /* Reserved room */ +}; + +/* + * IPC Response structure to logout user account. + */ +struct ksmbd_logout_request { + __s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */ + __u32 account_flags; + __u32 reserved[16]; /* Reserved room */ +}; + +/* + * RPC command structure to send rpc request like srvsvc or wkssvc to + * IPC user daemon. + */ +struct ksmbd_rpc_command { + __u32 handle; + __u32 flags; + __u32 payload_sz; + __u8 payload[]; +}; + +/* + * IPC Request Kerberos authentication + */ +struct ksmbd_spnego_authen_request { + __u32 handle; + __u16 spnego_blob_len; /* the length of spnego_blob */ + __u8 spnego_blob[0]; /* + * the GSS token from SecurityBuffer of + * SMB2 SESSION SETUP request + */ +}; + +/* + * Response data which includes the GSS token and the session key generated by + * user daemon. + */ +struct ksmbd_spnego_authen_response { + __u32 handle; + struct ksmbd_login_response login_response; /* + * the login response with + * a user identified by the + * GSS token from a client + */ + __u16 session_key_len; /* the length of the session key */ + __u16 spnego_blob_len; /* + * the length of the GSS token which will be + * stored in SecurityBuffer of SMB2 SESSION + * SETUP response + */ + __u8 payload[]; /* session key + AP_REP */ +}; + +/* + * This also used as NETLINK attribute type value. + * + * NOTE: + * Response message type value should be equal to + * request message type value + 1. + */ +enum ksmbd_event { + KSMBD_EVENT_UNSPEC = 0, + KSMBD_EVENT_HEARTBEAT_REQUEST, + + KSMBD_EVENT_STARTING_UP, + KSMBD_EVENT_SHUTTING_DOWN, + + KSMBD_EVENT_LOGIN_REQUEST, + KSMBD_EVENT_LOGIN_RESPONSE = 5, + + KSMBD_EVENT_SHARE_CONFIG_REQUEST, + KSMBD_EVENT_SHARE_CONFIG_RESPONSE, + + KSMBD_EVENT_TREE_CONNECT_REQUEST, + KSMBD_EVENT_TREE_CONNECT_RESPONSE, + + KSMBD_EVENT_TREE_DISCONNECT_REQUEST = 10, + + KSMBD_EVENT_LOGOUT_REQUEST, + + KSMBD_EVENT_RPC_REQUEST, + KSMBD_EVENT_RPC_RESPONSE, + + KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST, + KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE = 15, + + KSMBD_EVENT_MAX +}; + +/* + * Enumeration for IPC tree connect status. + */ +enum KSMBD_TREE_CONN_STATUS { + KSMBD_TREE_CONN_STATUS_OK = 0, + KSMBD_TREE_CONN_STATUS_NOMEM, + KSMBD_TREE_CONN_STATUS_NO_SHARE, + KSMBD_TREE_CONN_STATUS_NO_USER, + KSMBD_TREE_CONN_STATUS_INVALID_USER, + KSMBD_TREE_CONN_STATUS_HOST_DENIED = 5, + KSMBD_TREE_CONN_STATUS_CONN_EXIST, + KSMBD_TREE_CONN_STATUS_TOO_MANY_CONNS, + KSMBD_TREE_CONN_STATUS_TOO_MANY_SESSIONS, + KSMBD_TREE_CONN_STATUS_ERROR, +}; + +/* + * User config flags. + */ +#define KSMBD_USER_FLAG_INVALID (0) +#define KSMBD_USER_FLAG_OK BIT(0) +#define KSMBD_USER_FLAG_BAD_PASSWORD BIT(1) +#define KSMBD_USER_FLAG_BAD_UID BIT(2) +#define KSMBD_USER_FLAG_BAD_USER BIT(3) +#define KSMBD_USER_FLAG_GUEST_ACCOUNT BIT(4) +#define KSMBD_USER_FLAG_DELAY_SESSION BIT(5) + +/* + * Share config flags. + */ +#define KSMBD_SHARE_FLAG_INVALID (0) +#define KSMBD_SHARE_FLAG_AVAILABLE BIT(0) +#define KSMBD_SHARE_FLAG_BROWSEABLE BIT(1) +#define KSMBD_SHARE_FLAG_WRITEABLE BIT(2) +#define KSMBD_SHARE_FLAG_READONLY BIT(3) +#define KSMBD_SHARE_FLAG_GUEST_OK BIT(4) +#define KSMBD_SHARE_FLAG_GUEST_ONLY BIT(5) +#define KSMBD_SHARE_FLAG_STORE_DOS_ATTRS BIT(6) +#define KSMBD_SHARE_FLAG_OPLOCKS BIT(7) +#define KSMBD_SHARE_FLAG_PIPE BIT(8) +#define KSMBD_SHARE_FLAG_HIDE_DOT_FILES BIT(9) +#define KSMBD_SHARE_FLAG_INHERIT_OWNER BIT(10) +#define KSMBD_SHARE_FLAG_STREAMS BIT(11) +#define KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS BIT(12) +#define KSMBD_SHARE_FLAG_ACL_XATTR BIT(13) +#define KSMBD_SHARE_FLAG_UPDATE BIT(14) + +/* + * Tree connect request flags. + */ +#define KSMBD_TREE_CONN_FLAG_REQUEST_SMB1 (0) +#define KSMBD_TREE_CONN_FLAG_REQUEST_IPV6 BIT(0) +#define KSMBD_TREE_CONN_FLAG_REQUEST_SMB2 BIT(1) + +/* + * Tree connect flags. + */ +#define KSMBD_TREE_CONN_FLAG_GUEST_ACCOUNT BIT(0) +#define KSMBD_TREE_CONN_FLAG_READ_ONLY BIT(1) +#define KSMBD_TREE_CONN_FLAG_WRITABLE BIT(2) +#define KSMBD_TREE_CONN_FLAG_ADMIN_ACCOUNT BIT(3) +#define KSMBD_TREE_CONN_FLAG_UPDATE BIT(4) + +/* + * RPC over IPC. + */ +#define KSMBD_RPC_METHOD_RETURN BIT(0) +#define KSMBD_RPC_SRVSVC_METHOD_INVOKE BIT(1) +#define KSMBD_RPC_SRVSVC_METHOD_RETURN (KSMBD_RPC_SRVSVC_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_WKSSVC_METHOD_INVOKE BIT(2) +#define KSMBD_RPC_WKSSVC_METHOD_RETURN (KSMBD_RPC_WKSSVC_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_IOCTL_METHOD (BIT(3) | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_OPEN_METHOD BIT(4) +#define KSMBD_RPC_WRITE_METHOD BIT(5) +#define KSMBD_RPC_READ_METHOD (BIT(6) | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_CLOSE_METHOD BIT(7) +#define KSMBD_RPC_RAP_METHOD (BIT(8) | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_RESTRICTED_CONTEXT BIT(9) +#define KSMBD_RPC_SAMR_METHOD_INVOKE BIT(10) +#define KSMBD_RPC_SAMR_METHOD_RETURN (KSMBD_RPC_SAMR_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) +#define KSMBD_RPC_LSARPC_METHOD_INVOKE BIT(11) +#define KSMBD_RPC_LSARPC_METHOD_RETURN (KSMBD_RPC_LSARPC_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN) + +/* + * RPC status definitions. + */ +#define KSMBD_RPC_OK 0 +#define KSMBD_RPC_EBAD_FUNC 0x00000001 +#define KSMBD_RPC_EACCESS_DENIED 0x00000005 +#define KSMBD_RPC_EBAD_FID 0x00000006 +#define KSMBD_RPC_ENOMEM 0x00000008 +#define KSMBD_RPC_EBAD_DATA 0x0000000D +#define KSMBD_RPC_ENOTIMPLEMENTED 0x00000040 +#define KSMBD_RPC_EINVALID_PARAMETER 0x00000057 +#define KSMBD_RPC_EMORE_DATA 0x000000EA +#define KSMBD_RPC_EINVALID_LEVEL 0x0000007C +#define KSMBD_RPC_SOME_NOT_MAPPED 0x00000107 + +#define KSMBD_CONFIG_OPT_DISABLED 0 +#define KSMBD_CONFIG_OPT_ENABLED 1 +#define KSMBD_CONFIG_OPT_AUTO 2 +#define KSMBD_CONFIG_OPT_MANDATORY 3 + +#endif /* _LINUX_KSMBD_SERVER_H */ diff --git a/fs/ksmbd/ksmbd_spnego_negtokeninit.asn1 b/fs/ksmbd/ksmbd_spnego_negtokeninit.asn1 new file mode 100644 index 0000000000000..0065f191b54b7 --- /dev/null +++ b/fs/ksmbd/ksmbd_spnego_negtokeninit.asn1 @@ -0,0 +1,31 @@ +GSSAPI ::= + [APPLICATION 0] IMPLICIT SEQUENCE { + thisMech + OBJECT IDENTIFIER ({ksmbd_gssapi_this_mech}), + negotiationToken + NegotiationToken + } + +MechType ::= OBJECT IDENTIFIER ({ksmbd_neg_token_init_mech_type}) + +MechTypeList ::= SEQUENCE OF MechType + +NegTokenInit ::= + SEQUENCE { + mechTypes + [0] MechTypeList, + reqFlags + [1] BIT STRING OPTIONAL, + mechToken + [2] OCTET STRING OPTIONAL ({ksmbd_neg_token_init_mech_token}), + mechListMIC + [3] OCTET STRING OPTIONAL + } + +NegotiationToken ::= + CHOICE { + negTokenInit + [0] NegTokenInit, + negTokenTarg + [1] ANY + } diff --git a/fs/ksmbd/ksmbd_spnego_negtokentarg.asn1 b/fs/ksmbd/ksmbd_spnego_negtokentarg.asn1 new file mode 100644 index 0000000000000..1151933e7b9c5 --- /dev/null +++ b/fs/ksmbd/ksmbd_spnego_negtokentarg.asn1 @@ -0,0 +1,19 @@ +GSSAPI ::= + CHOICE { + negTokenInit + [0] ANY, + negTokenTarg + [1] NegTokenTarg + } + +NegTokenTarg ::= + SEQUENCE { + negResult + [0] ENUMERATED OPTIONAL, + supportedMech + [1] OBJECT IDENTIFIER OPTIONAL, + responseToken + [2] OCTET STRING OPTIONAL ({ksmbd_neg_token_targ_resp_token}), + mechListMIC + [3] OCTET STRING OPTIONAL + } diff --git a/fs/ksmbd/ksmbd_work.c b/fs/ksmbd/ksmbd_work.c new file mode 100644 index 0000000000000..14b9caebf7a4f --- /dev/null +++ b/fs/ksmbd/ksmbd_work.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include + +#include "server.h" +#include "connection.h" +#include "ksmbd_work.h" +#include "mgmt/ksmbd_ida.h" + +static struct kmem_cache *work_cache; +static struct workqueue_struct *ksmbd_wq; + +struct ksmbd_work *ksmbd_alloc_work_struct(void) +{ + struct ksmbd_work *work = kmem_cache_zalloc(work_cache, GFP_KERNEL); + + if (work) { + work->compound_fid = KSMBD_NO_FID; + work->compound_pfid = KSMBD_NO_FID; + INIT_LIST_HEAD(&work->request_entry); + INIT_LIST_HEAD(&work->async_request_entry); + INIT_LIST_HEAD(&work->fp_entry); + INIT_LIST_HEAD(&work->interim_entry); + } + return work; +} + +void ksmbd_free_work_struct(struct ksmbd_work *work) +{ + WARN_ON(work->saved_cred != NULL); + + kvfree(work->response_buf); + kvfree(work->aux_payload_buf); + kfree(work->tr_buf); + kvfree(work->request_buf); + if (work->async_id) + ksmbd_release_id(&work->conn->async_ida, work->async_id); + kmem_cache_free(work_cache, work); +} + +void ksmbd_work_pool_destroy(void) +{ + kmem_cache_destroy(work_cache); +} + +int ksmbd_work_pool_init(void) +{ + work_cache = kmem_cache_create("ksmbd_work_cache", + sizeof(struct ksmbd_work), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (!work_cache) + return -ENOMEM; + return 0; +} + +int ksmbd_workqueue_init(void) +{ + ksmbd_wq = alloc_workqueue("ksmbd-io", 0, 0); + if (!ksmbd_wq) + return -ENOMEM; + return 0; +} + +void ksmbd_workqueue_destroy(void) +{ + destroy_workqueue(ksmbd_wq); + ksmbd_wq = NULL; +} + +bool ksmbd_queue_work(struct ksmbd_work *work) +{ + return queue_work(ksmbd_wq, &work->work); +} diff --git a/fs/ksmbd/ksmbd_work.h b/fs/ksmbd/ksmbd_work.h new file mode 100644 index 0000000000000..3234f2cf6327c --- /dev/null +++ b/fs/ksmbd/ksmbd_work.h @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_WORK_H__ +#define __KSMBD_WORK_H__ + +#include +#include + +struct ksmbd_conn; +struct ksmbd_session; +struct ksmbd_tree_connect; + +enum { + KSMBD_WORK_ACTIVE = 0, + KSMBD_WORK_CANCELLED, + KSMBD_WORK_CLOSED, +}; + +/* one of these for every pending CIFS request at the connection */ +struct ksmbd_work { + /* Server corresponding to this mid */ + struct ksmbd_conn *conn; + struct ksmbd_session *sess; + struct ksmbd_tree_connect *tcon; + + /* Pointer to received SMB header */ + void *request_buf; + /* Response buffer */ + void *response_buf; + + /* Read data buffer */ + void *aux_payload_buf; + + /* Next cmd hdr in compound req buf*/ + int next_smb2_rcv_hdr_off; + /* Next cmd hdr in compound rsp buf*/ + int next_smb2_rsp_hdr_off; + + /* + * Current Local FID assigned compound response if SMB2 CREATE + * command is present in compound request + */ + u64 compound_fid; + u64 compound_pfid; + u64 compound_sid; + + const struct cred *saved_cred; + + /* Number of granted credits */ + unsigned int credits_granted; + + /* response smb header size */ + unsigned int resp_hdr_sz; + unsigned int response_sz; + /* Read data count */ + unsigned int aux_payload_sz; + + void *tr_buf; + + unsigned char state; + /* Multiple responses for one request e.g. SMB ECHO */ + bool multiRsp:1; + /* No response for cancelled request */ + bool send_no_response:1; + /* Request is encrypted */ + bool encrypted:1; + /* Is this SYNC or ASYNC ksmbd_work */ + bool synchronous:1; + bool need_invalidate_rkey:1; + + unsigned int remote_key; + /* cancel works */ + int async_id; + void **cancel_argv; + void (*cancel_fn)(void **argv); + + struct work_struct work; + /* List head at conn->requests */ + struct list_head request_entry; + /* List head at conn->async_requests */ + struct list_head async_request_entry; + struct list_head fp_entry; + struct list_head interim_entry; +}; + +/** + * ksmbd_resp_buf_next - Get next buffer on compound response. + * @work: smb work containing response buffer + */ +static inline void *ksmbd_resp_buf_next(struct ksmbd_work *work) +{ + return work->response_buf + work->next_smb2_rsp_hdr_off + 4; +} + +/** + * ksmbd_req_buf_next - Get next buffer on compound request. + * @work: smb work containing response buffer + */ +static inline void *ksmbd_req_buf_next(struct ksmbd_work *work) +{ + return work->request_buf + work->next_smb2_rcv_hdr_off + 4; +} + +struct ksmbd_work *ksmbd_alloc_work_struct(void); +void ksmbd_free_work_struct(struct ksmbd_work *work); + +void ksmbd_work_pool_destroy(void); +int ksmbd_work_pool_init(void); + +int ksmbd_workqueue_init(void); +void ksmbd_workqueue_destroy(void); +bool ksmbd_queue_work(struct ksmbd_work *work); + +#endif /* __KSMBD_WORK_H__ */ diff --git a/fs/ksmbd/mgmt/ksmbd_ida.c b/fs/ksmbd/mgmt/ksmbd_ida.c new file mode 100644 index 0000000000000..f70833c819976 --- /dev/null +++ b/fs/ksmbd/mgmt/ksmbd_ida.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include "ksmbd_ida.h" + +static inline int __acquire_id(struct ida *ida, int from, int to) +{ + return ida_simple_get(ida, from, to, GFP_KERNEL); +} + +#ifdef CONFIG_SMB_INSECURE_SERVER +int ksmbd_acquire_smb1_tid(struct ida *ida) +{ + return __acquire_id(ida, 1, 0xFFFF); +} +#endif + +int ksmbd_acquire_smb2_tid(struct ida *ida) +{ + return __acquire_id(ida, 1, 0xFFFFFFFF); + +} + +#ifdef CONFIG_SMB_INSECURE_SERVER +int ksmbd_acquire_smb1_uid(struct ida *ida) +{ + return __acquire_id(ida, 1, 0xFFFE); +} +#endif + +int ksmbd_acquire_smb2_uid(struct ida *ida) +{ + int id; + + id = __acquire_id(ida, 1, 0); + if (id == 0xFFFE) + id = __acquire_id(ida, 1, 0); + + return id; +} + +int ksmbd_acquire_async_msg_id(struct ida *ida) +{ + return __acquire_id(ida, 1, 0); +} + +int ksmbd_acquire_id(struct ida *ida) +{ + return __acquire_id(ida, 0, 0); +} + +void ksmbd_release_id(struct ida *ida, int id) +{ + ida_simple_remove(ida, id); +} diff --git a/fs/ksmbd/mgmt/ksmbd_ida.h b/fs/ksmbd/mgmt/ksmbd_ida.h new file mode 100644 index 0000000000000..e7df7786f431c --- /dev/null +++ b/fs/ksmbd/mgmt/ksmbd_ida.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_IDA_MANAGEMENT_H__ +#define __KSMBD_IDA_MANAGEMENT_H__ + +#include +#include + +/* + * 2.2.1.6.7 TID Generation + * The value 0xFFFF MUST NOT be used as a valid TID. All other + * possible values for TID, including zero (0x0000), are valid. + * The value 0xFFFF is used to specify all TIDs or no TID, + * depending upon the context in which it is used. + */ +#ifdef CONFIG_SMB_INSECURE_SERVER +int ksmbd_acquire_smb1_tid(struct ida *ida); +#endif +int ksmbd_acquire_smb2_tid(struct ida *ida); + +/* + * 2.2.1.6.8 UID Generation + * The value 0xFFFE was declared reserved in the LAN Manager 1.0 + * documentation, so a value of 0xFFFE SHOULD NOT be used as a + * valid UID.<21> All other possible values for a UID, excluding + * zero (0x0000), are valid. + */ +#ifdef CONFIG_SMB_INSECURE_SERVER +int ksmbd_acquire_smb1_uid(struct ida *ida); +#endif +int ksmbd_acquire_smb2_uid(struct ida *ida); +int ksmbd_acquire_async_msg_id(struct ida *ida); + +int ksmbd_acquire_id(struct ida *ida); + +void ksmbd_release_id(struct ida *ida, int id); +#endif /* __KSMBD_IDA_MANAGEMENT_H__ */ diff --git a/fs/ksmbd/mgmt/share_config.c b/fs/ksmbd/mgmt/share_config.c new file mode 100644 index 0000000000000..328a412259dc1 --- /dev/null +++ b/fs/ksmbd/mgmt/share_config.c @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "share_config.h" +#include "user_config.h" +#include "user_session.h" +#include "../transport_ipc.h" +#include "../misc.h" + +#define SHARE_HASH_BITS 3 +static DEFINE_HASHTABLE(shares_table, SHARE_HASH_BITS); +static DECLARE_RWSEM(shares_table_lock); + +struct ksmbd_veto_pattern { + char *pattern; + struct list_head list; +}; + +static unsigned int share_name_hash(const char *name) +{ + return jhash(name, strlen(name), 0); +} + +static void kill_share(struct ksmbd_share_config *share) +{ + while (!list_empty(&share->veto_list)) { + struct ksmbd_veto_pattern *p; + + p = list_entry(share->veto_list.next, + struct ksmbd_veto_pattern, + list); + list_del(&p->list); + kfree(p->pattern); + kfree(p); + } + + if (share->path) + path_put(&share->vfs_path); + kfree(share->name); + kfree(share->path); + kfree(share); +} + +void ksmbd_share_config_del(struct ksmbd_share_config *share) +{ + down_write(&shares_table_lock); + hash_del(&share->hlist); + up_write(&shares_table_lock); +} + +void __ksmbd_share_config_put(struct ksmbd_share_config *share) +{ + ksmbd_share_config_del(share); + kill_share(share); +} + +static struct ksmbd_share_config * +__get_share_config(struct ksmbd_share_config *share) +{ + if (!atomic_inc_not_zero(&share->refcount)) + return NULL; + return share; +} + +static struct ksmbd_share_config *__share_lookup(const char *name) +{ + struct ksmbd_share_config *share; + unsigned int key = share_name_hash(name); + + hash_for_each_possible(shares_table, share, hlist, key) { + if (!strcmp(name, share->name)) + return share; + } + return NULL; +} + +static int parse_veto_list(struct ksmbd_share_config *share, + char *veto_list, + int veto_list_sz) +{ + int sz = 0; + + if (!veto_list_sz) + return 0; + + while (veto_list_sz > 0) { + struct ksmbd_veto_pattern *p; + + sz = strlen(veto_list); + if (!sz) + break; + + p = kzalloc(sizeof(struct ksmbd_veto_pattern), GFP_KERNEL); + if (!p) + return -ENOMEM; + + p->pattern = kstrdup(veto_list, GFP_KERNEL); + if (!p->pattern) { + kfree(p); + return -ENOMEM; + } + + list_add(&p->list, &share->veto_list); + + veto_list += sz + 1; + veto_list_sz -= (sz + 1); + } + + return 0; +} + +static struct ksmbd_share_config *share_config_request(struct unicode_map *um, + const char *name) +{ + struct ksmbd_share_config_response *resp; + struct ksmbd_share_config *share = NULL; + struct ksmbd_share_config *lookup; + int ret; + + resp = ksmbd_ipc_share_config_request(name); + if (!resp) + return NULL; + + if (resp->flags == KSMBD_SHARE_FLAG_INVALID) + goto out; + + if (*resp->share_name) { + char *cf_resp_name; + bool equal; + + cf_resp_name = ksmbd_casefold_sharename(um, resp->share_name); + if (IS_ERR(cf_resp_name)) + goto out; + equal = !strcmp(cf_resp_name, name); + kfree(cf_resp_name); + if (!equal) + goto out; + } + + share = kzalloc(sizeof(struct ksmbd_share_config), GFP_KERNEL); + if (!share) + goto out; + + share->flags = resp->flags; + atomic_set(&share->refcount, 1); + INIT_LIST_HEAD(&share->veto_list); + share->name = kstrdup(name, GFP_KERNEL); + + if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { + share->path = kstrdup(ksmbd_share_config_path(resp), + GFP_KERNEL); + if (share->path) + share->path_sz = strlen(share->path); + share->create_mask = resp->create_mask; + share->directory_mask = resp->directory_mask; + share->force_create_mode = resp->force_create_mode; + share->force_directory_mode = resp->force_directory_mode; + share->force_uid = resp->force_uid; + share->force_gid = resp->force_gid; + ret = parse_veto_list(share, + KSMBD_SHARE_CONFIG_VETO_LIST(resp), + resp->veto_list_sz); + if (!ret && share->path) { + ret = kern_path(share->path, 0, &share->vfs_path); + if (ret) { + ksmbd_debug(SMB, "failed to access '%s'\n", + share->path); + /* Avoid put_path() */ + kfree(share->path); + share->path = NULL; + } + } + if (ret || !share->name) { + kill_share(share); + share = NULL; + goto out; + } + } + + down_write(&shares_table_lock); + lookup = __share_lookup(name); + if (lookup) + lookup = __get_share_config(lookup); + if (!lookup) { + hash_add(shares_table, &share->hlist, share_name_hash(name)); + } else { + kill_share(share); + share = lookup; + } + up_write(&shares_table_lock); + +out: + kvfree(resp); + return share; +} + +struct ksmbd_share_config *ksmbd_share_config_get(struct unicode_map *um, + const char *name) +{ + struct ksmbd_share_config *share; + + down_read(&shares_table_lock); + share = __share_lookup(name); + if (share) + share = __get_share_config(share); + up_read(&shares_table_lock); + + if (share) + return share; + return share_config_request(um, name); +} + +bool ksmbd_share_veto_filename(struct ksmbd_share_config *share, + const char *filename) +{ + struct ksmbd_veto_pattern *p; + + list_for_each_entry(p, &share->veto_list, list) { + if (match_wildcard(p->pattern, filename)) + return true; + } + return false; +} diff --git a/fs/ksmbd/mgmt/share_config.h b/fs/ksmbd/mgmt/share_config.h new file mode 100644 index 0000000000000..3fd3382939421 --- /dev/null +++ b/fs/ksmbd/mgmt/share_config.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __SHARE_CONFIG_MANAGEMENT_H__ +#define __SHARE_CONFIG_MANAGEMENT_H__ + +#include +#include +#include +#include + +struct ksmbd_share_config { + char *name; + char *path; + + unsigned int path_sz; + unsigned int flags; + struct list_head veto_list; + + struct path vfs_path; + + atomic_t refcount; + struct hlist_node hlist; + unsigned short create_mask; + unsigned short directory_mask; + unsigned short force_create_mode; + unsigned short force_directory_mode; + unsigned short force_uid; + unsigned short force_gid; +}; + +#define KSMBD_SHARE_INVALID_UID ((__u16)-1) +#define KSMBD_SHARE_INVALID_GID ((__u16)-1) + +static inline int share_config_create_mode(struct ksmbd_share_config *share, + umode_t posix_mode) +{ + if (!share->force_create_mode) { + if (!posix_mode) + return share->create_mask; + else + return posix_mode & share->create_mask; + } + return share->force_create_mode & share->create_mask; +} + +static inline int share_config_directory_mode(struct ksmbd_share_config *share, + umode_t posix_mode) +{ + if (!share->force_directory_mode) { + if (!posix_mode) + return share->directory_mask; + else + return posix_mode & share->directory_mask; + } + + return share->force_directory_mode & share->directory_mask; +} + +static inline int test_share_config_flag(struct ksmbd_share_config *share, + int flag) +{ + return share->flags & flag; +} + +void ksmbd_share_config_del(struct ksmbd_share_config *share); +void __ksmbd_share_config_put(struct ksmbd_share_config *share); + +static inline void ksmbd_share_config_put(struct ksmbd_share_config *share) +{ + if (!atomic_dec_and_test(&share->refcount)) + return; + __ksmbd_share_config_put(share); +} + +struct ksmbd_share_config *ksmbd_share_config_get(struct unicode_map *um, + const char *name); +bool ksmbd_share_veto_filename(struct ksmbd_share_config *share, + const char *filename); +#endif /* __SHARE_CONFIG_MANAGEMENT_H__ */ diff --git a/fs/ksmbd/mgmt/tree_connect.c b/fs/ksmbd/mgmt/tree_connect.c new file mode 100644 index 0000000000000..8ce17b3fb8dad --- /dev/null +++ b/fs/ksmbd/mgmt/tree_connect.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include + +#include "../transport_ipc.h" +#include "../connection.h" + +#include "tree_connect.h" +#include "user_config.h" +#include "share_config.h" +#include "user_session.h" + +struct ksmbd_tree_conn_status +ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, + const char *share_name) +{ + struct ksmbd_tree_conn_status status = {-ENOENT, NULL}; + struct ksmbd_tree_connect_response *resp = NULL; + struct ksmbd_share_config *sc; + struct ksmbd_tree_connect *tree_conn = NULL; + struct sockaddr *peer_addr; + int ret; + + sc = ksmbd_share_config_get(conn->um, share_name); + if (!sc) + return status; + + tree_conn = kzalloc(sizeof(struct ksmbd_tree_connect), GFP_KERNEL); + if (!tree_conn) { + status.ret = -ENOMEM; + goto out_error; + } + + tree_conn->id = ksmbd_acquire_tree_conn_id(sess); + if (tree_conn->id < 0) { + status.ret = -EINVAL; + goto out_error; + } + + peer_addr = KSMBD_TCP_PEER_SOCKADDR(conn); + resp = ksmbd_ipc_tree_connect_request(sess, + sc, + tree_conn, + peer_addr); + if (!resp) { + status.ret = -EINVAL; + goto out_error; + } + + status.ret = resp->status; + if (status.ret != KSMBD_TREE_CONN_STATUS_OK) + goto out_error; + + tree_conn->flags = resp->connection_flags; + if (test_tree_conn_flag(tree_conn, KSMBD_TREE_CONN_FLAG_UPDATE)) { + struct ksmbd_share_config *new_sc; + + ksmbd_share_config_del(sc); + new_sc = ksmbd_share_config_get(conn->um, share_name); + if (!new_sc) { + pr_err("Failed to update stale share config\n"); + status.ret = -ESTALE; + goto out_error; + } + ksmbd_share_config_put(sc); + sc = new_sc; + } + + tree_conn->user = sess->user; + tree_conn->share_conf = sc; + status.tree_conn = tree_conn; + + ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn, + GFP_KERNEL)); + if (ret) { + status.ret = -ENOMEM; + goto out_error; + } + kvfree(resp); + return status; + +out_error: + if (tree_conn) + ksmbd_release_tree_conn_id(sess, tree_conn->id); + ksmbd_share_config_put(sc); + kfree(tree_conn); + kvfree(resp); + return status; +} + +int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, + struct ksmbd_tree_connect *tree_conn) +{ + int ret; + + ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id); + ksmbd_release_tree_conn_id(sess, tree_conn->id); + xa_erase(&sess->tree_conns, tree_conn->id); + ksmbd_share_config_put(tree_conn->share_conf); + kfree(tree_conn); + return ret; +} + +struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, + unsigned int id) +{ + return xa_load(&sess->tree_conns, id); +} + +struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess, + unsigned int id) +{ + struct ksmbd_tree_connect *tc; + + tc = ksmbd_tree_conn_lookup(sess, id); + if (tc) + return tc->share_conf; + return NULL; +} + +int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess) +{ + int ret = 0; + struct ksmbd_tree_connect *tc; + unsigned long id; + + xa_for_each(&sess->tree_conns, id, tc) + ret |= ksmbd_tree_conn_disconnect(sess, tc); + xa_destroy(&sess->tree_conns); + return ret; +} diff --git a/fs/ksmbd/mgmt/tree_connect.h b/fs/ksmbd/mgmt/tree_connect.h new file mode 100644 index 0000000000000..0f97ddc1e39c0 --- /dev/null +++ b/fs/ksmbd/mgmt/tree_connect.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __TREE_CONNECT_MANAGEMENT_H__ +#define __TREE_CONNECT_MANAGEMENT_H__ + +#include + +#include "../ksmbd_netlink.h" + +struct ksmbd_share_config; +struct ksmbd_user; +struct ksmbd_conn; + +struct ksmbd_tree_connect { + int id; + + unsigned int flags; + struct ksmbd_share_config *share_conf; + struct ksmbd_user *user; + + struct list_head list; + + int maximal_access; + bool posix_extensions; +}; + +struct ksmbd_tree_conn_status { + unsigned int ret; + struct ksmbd_tree_connect *tree_conn; +}; + +static inline int test_tree_conn_flag(struct ksmbd_tree_connect *tree_conn, + int flag) +{ + return tree_conn->flags & flag; +} + +struct ksmbd_session; + +struct ksmbd_tree_conn_status +ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, + const char *share_name); + +int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, + struct ksmbd_tree_connect *tree_conn); + +struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, + unsigned int id); + +struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess, + unsigned int id); + +int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess); + +#endif /* __TREE_CONNECT_MANAGEMENT_H__ */ diff --git a/fs/ksmbd/mgmt/user_config.c b/fs/ksmbd/mgmt/user_config.c new file mode 100644 index 0000000000000..279d00feff216 --- /dev/null +++ b/fs/ksmbd/mgmt/user_config.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include + +#include "user_config.h" +#include "../transport_ipc.h" + +struct ksmbd_user *ksmbd_login_user(const char *account) +{ + struct ksmbd_login_response *resp; + struct ksmbd_user *user = NULL; + + resp = ksmbd_ipc_login_request(account); + if (!resp) + return NULL; + + if (!(resp->status & KSMBD_USER_FLAG_OK)) + goto out; + + user = ksmbd_alloc_user(resp); +out: + kvfree(resp); + return user; +} + +struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp) +{ + struct ksmbd_user *user = NULL; + + user = kmalloc(sizeof(struct ksmbd_user), GFP_KERNEL); + if (!user) + return NULL; + + user->name = kstrdup(resp->account, GFP_KERNEL); + user->flags = resp->status; + user->gid = resp->gid; + user->uid = resp->uid; + user->passkey_sz = resp->hash_sz; + user->passkey = kmalloc(resp->hash_sz, GFP_KERNEL); + if (user->passkey) + memcpy(user->passkey, resp->hash, resp->hash_sz); + + if (!user->name || !user->passkey) { + kfree(user->name); + kfree(user->passkey); + kfree(user); + user = NULL; + } + return user; +} + +void ksmbd_free_user(struct ksmbd_user *user) +{ + ksmbd_ipc_logout_request(user->name, user->flags); + kfree(user->name); + kfree(user->passkey); + kfree(user); +} + +int ksmbd_anonymous_user(struct ksmbd_user *user) +{ + if (user->name[0] == '\0') + return 1; + return 0; +} + +bool ksmbd_compare_user(struct ksmbd_user *u1, struct ksmbd_user *u2) +{ + if (strcmp(u1->name, u2->name)) + return false; + if (memcmp(u1->passkey, u2->passkey, u1->passkey_sz)) + return false; + + return true; +} diff --git a/fs/ksmbd/mgmt/user_config.h b/fs/ksmbd/mgmt/user_config.h new file mode 100644 index 0000000000000..6a44109617f14 --- /dev/null +++ b/fs/ksmbd/mgmt/user_config.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __USER_CONFIG_MANAGEMENT_H__ +#define __USER_CONFIG_MANAGEMENT_H__ + +#include "../glob.h" + +struct ksmbd_user { + unsigned short flags; + + unsigned int uid; + unsigned int gid; + + char *name; + + size_t passkey_sz; + char *passkey; + unsigned int failed_login_count; +}; + +static inline bool user_guest(struct ksmbd_user *user) +{ + return user->flags & KSMBD_USER_FLAG_GUEST_ACCOUNT; +} + +static inline void set_user_flag(struct ksmbd_user *user, int flag) +{ + user->flags |= flag; +} + +static inline int test_user_flag(struct ksmbd_user *user, int flag) +{ + return user->flags & flag; +} + +static inline void set_user_guest(struct ksmbd_user *user) +{ +} + +static inline char *user_passkey(struct ksmbd_user *user) +{ + return user->passkey; +} + +static inline char *user_name(struct ksmbd_user *user) +{ + return user->name; +} + +static inline unsigned int user_uid(struct ksmbd_user *user) +{ + return user->uid; +} + +static inline unsigned int user_gid(struct ksmbd_user *user) +{ + return user->gid; +} + +struct ksmbd_user *ksmbd_login_user(const char *account); +struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp); +void ksmbd_free_user(struct ksmbd_user *user); +int ksmbd_anonymous_user(struct ksmbd_user *user); +bool ksmbd_compare_user(struct ksmbd_user *u1, struct ksmbd_user *u2); +#endif /* __USER_CONFIG_MANAGEMENT_H__ */ diff --git a/fs/ksmbd/mgmt/user_session.c b/fs/ksmbd/mgmt/user_session.c new file mode 100644 index 0000000000000..50497e78bbe35 --- /dev/null +++ b/fs/ksmbd/mgmt/user_session.c @@ -0,0 +1,402 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include + +#include "ksmbd_ida.h" +#include "user_session.h" +#include "user_config.h" +#include "tree_connect.h" +#include "../transport_ipc.h" +#include "../connection.h" +#include "../vfs_cache.h" + +static DEFINE_IDA(session_ida); + +#define SESSION_HASH_BITS 3 +static DEFINE_HASHTABLE(sessions_table, SESSION_HASH_BITS); +static DECLARE_RWSEM(sessions_table_lock); + +struct ksmbd_session_rpc { + int id; + unsigned int method; +}; + +static void free_channel_list(struct ksmbd_session *sess) +{ + struct channel *chann; + unsigned long index; + + xa_for_each(&sess->ksmbd_chann_list, index, chann) { + xa_erase(&sess->ksmbd_chann_list, index); + kfree(chann); + } + + xa_destroy(&sess->ksmbd_chann_list); +} + +static void __session_rpc_close(struct ksmbd_session *sess, + struct ksmbd_session_rpc *entry) +{ + struct ksmbd_rpc_command *resp; + + resp = ksmbd_rpc_close(sess, entry->id); + if (!resp) + pr_err("Unable to close RPC pipe %d\n", entry->id); + + kvfree(resp); + ksmbd_rpc_id_free(entry->id); + kfree(entry); +} + +static void ksmbd_session_rpc_clear_list(struct ksmbd_session *sess) +{ + struct ksmbd_session_rpc *entry; + long index; + + xa_for_each(&sess->rpc_handle_list, index, entry) { + xa_erase(&sess->rpc_handle_list, index); + __session_rpc_close(sess, entry); + } + + xa_destroy(&sess->rpc_handle_list); +} + +static int __rpc_method(char *rpc_name) +{ + if (!strcmp(rpc_name, "\\srvsvc") || !strcmp(rpc_name, "srvsvc")) + return KSMBD_RPC_SRVSVC_METHOD_INVOKE; + + if (!strcmp(rpc_name, "\\wkssvc") || !strcmp(rpc_name, "wkssvc")) + return KSMBD_RPC_WKSSVC_METHOD_INVOKE; + + if (!strcmp(rpc_name, "LANMAN") || !strcmp(rpc_name, "lanman")) + return KSMBD_RPC_RAP_METHOD; + + if (!strcmp(rpc_name, "\\samr") || !strcmp(rpc_name, "samr")) + return KSMBD_RPC_SAMR_METHOD_INVOKE; + + if (!strcmp(rpc_name, "\\lsarpc") || !strcmp(rpc_name, "lsarpc")) + return KSMBD_RPC_LSARPC_METHOD_INVOKE; + + pr_err("Unsupported RPC: %s\n", rpc_name); + return 0; +} + +int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name) +{ + struct ksmbd_session_rpc *entry; + struct ksmbd_rpc_command *resp; + int method; + + method = __rpc_method(rpc_name); + if (!method) + return -EINVAL; + + entry = kzalloc(sizeof(struct ksmbd_session_rpc), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + entry->method = method; + entry->id = ksmbd_ipc_id_alloc(); + if (entry->id < 0) + goto free_entry; + xa_store(&sess->rpc_handle_list, entry->id, entry, GFP_KERNEL); + + resp = ksmbd_rpc_open(sess, entry->id); + if (!resp) + goto free_id; + + kvfree(resp); + return entry->id; +free_id: + xa_erase(&sess->rpc_handle_list, entry->id); + ksmbd_rpc_id_free(entry->id); +free_entry: + kfree(entry); + return -EINVAL; +} + +void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id) +{ + struct ksmbd_session_rpc *entry; + + entry = xa_erase(&sess->rpc_handle_list, id); + if (entry) + __session_rpc_close(sess, entry); +} + +int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id) +{ + struct ksmbd_session_rpc *entry; + + entry = xa_load(&sess->rpc_handle_list, id); + return entry ? entry->method : 0; +} + +void ksmbd_session_destroy(struct ksmbd_session *sess) +{ + if (!sess) + return; + +#ifdef CONFIG_SMB_INSECURE_SERVER + if (hash_hashed(&sess->hlist)) { + down_write(&sessions_table_lock); + hash_del(&sess->hlist); + up_write(&sessions_table_lock); + } +#else + down_write(&sessions_table_lock); + hash_del(&sess->hlist); + up_write(&sessions_table_lock); +#endif + + if (sess->user) + ksmbd_free_user(sess->user); + + ksmbd_tree_conn_session_logoff(sess); + ksmbd_destroy_file_table(&sess->file_table); + ksmbd_session_rpc_clear_list(sess); + free_channel_list(sess); + kfree(sess->Preauth_HashValue); + ksmbd_release_id(&session_ida, sess->id); + kfree(sess); +} + +static struct ksmbd_session *__session_lookup(unsigned long long id) +{ + struct ksmbd_session *sess; + + hash_for_each_possible(sessions_table, sess, hlist, id) { + if (id == sess->id) + return sess; + } + return NULL; +} + +int ksmbd_session_register(struct ksmbd_conn *conn, + struct ksmbd_session *sess) +{ + sess->dialect = conn->dialect; + memcpy(sess->ClientGUID, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); + return xa_err(xa_store(&conn->sessions, sess->id, sess, GFP_KERNEL)); +} + +static int ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess) +{ + struct channel *chann; + + chann = xa_erase(&sess->ksmbd_chann_list, (long)conn); + if (!chann) + return -ENOENT; + + kfree(chann); + + return 0; +} + +void ksmbd_sessions_deregister(struct ksmbd_conn *conn) +{ + struct ksmbd_session *sess; + + if (conn->binding) { + int bkt; + + down_write(&sessions_table_lock); + hash_for_each(sessions_table, bkt, sess, hlist) { + if (!ksmbd_chann_del(conn, sess)) { + up_write(&sessions_table_lock); + goto sess_destroy; + } + } + up_write(&sessions_table_lock); + } else { + unsigned long id; + + xa_for_each(&conn->sessions, id, sess) { + if (!ksmbd_chann_del(conn, sess)) + goto sess_destroy; + } + } + + return; + +sess_destroy: + if (xa_empty(&sess->ksmbd_chann_list)) { + xa_erase(&conn->sessions, sess->id); + ksmbd_session_destroy(sess); + } +} + +struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, + unsigned long long id) +{ + return xa_load(&conn->sessions, id); +} + +struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id) +{ + struct ksmbd_session *sess; + + down_read(&sessions_table_lock); + sess = __session_lookup(id); + up_read(&sessions_table_lock); + + return sess; +} + +struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn, + unsigned long long id) +{ + struct ksmbd_session *sess; + + sess = ksmbd_session_lookup(conn, id); + if (!sess && conn->binding) + sess = ksmbd_session_lookup_slowpath(id); + if (sess && sess->state != SMB2_SESSION_VALID) + sess = NULL; + return sess; +} + +struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn, + u64 sess_id) +{ + struct preauth_session *sess; + + sess = kmalloc(sizeof(struct preauth_session), GFP_KERNEL); + if (!sess) + return NULL; + + sess->id = sess_id; + memcpy(sess->Preauth_HashValue, conn->preauth_info->Preauth_HashValue, + PREAUTH_HASHVALUE_SIZE); + list_add(&sess->preauth_entry, &conn->preauth_sess_table); + + return sess; +} + +static bool ksmbd_preauth_session_id_match(struct preauth_session *sess, + unsigned long long id) +{ + return sess->id == id; +} + +struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn, + unsigned long long id) +{ + struct preauth_session *sess = NULL; + + list_for_each_entry(sess, &conn->preauth_sess_table, preauth_entry) { + if (ksmbd_preauth_session_id_match(sess, id)) + return sess; + } + return NULL; +} + +#ifdef CONFIG_SMB_INSECURE_SERVER +static int __init_smb1_session(struct ksmbd_session *sess) +{ + int id = ksmbd_acquire_smb1_uid(&session_ida); + + if (id < 0) + return -EINVAL; + sess->id = id; + return 0; +} +#endif + +static int __init_smb2_session(struct ksmbd_session *sess) +{ + int id = ksmbd_acquire_smb2_uid(&session_ida); + + if (id < 0) + return -EINVAL; + sess->id = id; + return 0; +} + +static struct ksmbd_session *__session_create(int protocol) +{ + struct ksmbd_session *sess; + int ret; + + sess = kzalloc(sizeof(struct ksmbd_session), GFP_KERNEL); + if (!sess) + return NULL; + + if (ksmbd_init_file_table(&sess->file_table)) + goto error; + + set_session_flag(sess, protocol); + xa_init(&sess->tree_conns); + xa_init(&sess->ksmbd_chann_list); + xa_init(&sess->rpc_handle_list); + sess->sequence_number = 1; + + switch (protocol) { +#ifdef CONFIG_SMB_INSECURE_SERVER + case CIFDS_SESSION_FLAG_SMB1: + ret = __init_smb1_session(sess); + break; +#endif + case CIFDS_SESSION_FLAG_SMB2: + ret = __init_smb2_session(sess); + break; + default: + ret = -EINVAL; + break; + } + + if (ret) + goto error; + + ida_init(&sess->tree_conn_ida); + + if (protocol == CIFDS_SESSION_FLAG_SMB2) { + down_write(&sessions_table_lock); + hash_add(sessions_table, &sess->hlist, sess->id); + up_write(&sessions_table_lock); + } + return sess; + +error: + ksmbd_session_destroy(sess); + return NULL; +} + +#ifdef CONFIG_SMB_INSECURE_SERVER +struct ksmbd_session *ksmbd_smb1_session_create(void) +{ + return __session_create(CIFDS_SESSION_FLAG_SMB1); +} +#endif + +struct ksmbd_session *ksmbd_smb2_session_create(void) +{ + return __session_create(CIFDS_SESSION_FLAG_SMB2); +} + +int ksmbd_acquire_tree_conn_id(struct ksmbd_session *sess) +{ + int id = -EINVAL; + +#ifdef CONFIG_SMB_INSECURE_SERVER + if (test_session_flag(sess, CIFDS_SESSION_FLAG_SMB1)) + id = ksmbd_acquire_smb1_tid(&sess->tree_conn_ida); +#endif + if (test_session_flag(sess, CIFDS_SESSION_FLAG_SMB2)) + id = ksmbd_acquire_smb2_tid(&sess->tree_conn_ida); + + return id; +} + +void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id) +{ + if (id >= 0) + ksmbd_release_id(&sess->tree_conn_ida, id); +} diff --git a/fs/ksmbd/mgmt/user_session.h b/fs/ksmbd/mgmt/user_session.h new file mode 100644 index 0000000000000..e9e6162bdb3f9 --- /dev/null +++ b/fs/ksmbd/mgmt/user_session.h @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __USER_SESSION_MANAGEMENT_H__ +#define __USER_SESSION_MANAGEMENT_H__ + +#include +#include + +#include "../smb_common.h" +#include "../ntlmssp.h" + +#ifdef CONFIG_SMB_INSECURE_SERVER +#define CIFDS_SESSION_FLAG_SMB1 BIT(0) +#endif +#define CIFDS_SESSION_FLAG_SMB2 BIT(1) + +#define PREAUTH_HASHVALUE_SIZE 64 + +struct ksmbd_file_table; + +struct channel { + __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; + struct ksmbd_conn *conn; +}; + +struct preauth_session { + __u8 Preauth_HashValue[PREAUTH_HASHVALUE_SIZE]; + u64 id; + struct list_head preauth_entry; +}; + +struct ksmbd_session { + u64 id; + + __u16 dialect; + char ClientGUID[SMB2_CLIENT_GUID_SIZE]; + + struct ksmbd_user *user; + unsigned int sequence_number; + unsigned int flags; + + bool sign; + bool enc; + bool is_anonymous; + + int state; + __u8 *Preauth_HashValue; + + char sess_key[CIFS_KEY_SIZE]; + + struct hlist_node hlist; + struct xarray ksmbd_chann_list; + struct xarray tree_conns; + struct ida tree_conn_ida; + struct xarray rpc_handle_list; + + __u8 smb3encryptionkey[SMB3_ENC_DEC_KEY_SIZE]; + __u8 smb3decryptionkey[SMB3_ENC_DEC_KEY_SIZE]; + __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; + + struct ksmbd_file_table file_table; +}; + +static inline int test_session_flag(struct ksmbd_session *sess, int bit) +{ + return sess->flags & bit; +} + +static inline void set_session_flag(struct ksmbd_session *sess, int bit) +{ + sess->flags |= bit; +} + +static inline void clear_session_flag(struct ksmbd_session *sess, int bit) +{ + sess->flags &= ~bit; +} + +#ifdef CONFIG_SMB_INSECURE_SERVER +struct ksmbd_session *ksmbd_smb1_session_create(void); +#endif +struct ksmbd_session *ksmbd_smb2_session_create(void); + +void ksmbd_session_destroy(struct ksmbd_session *sess); + +struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id); +struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, + unsigned long long id); +int ksmbd_session_register(struct ksmbd_conn *conn, + struct ksmbd_session *sess); +void ksmbd_sessions_deregister(struct ksmbd_conn *conn); +struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn, + unsigned long long id); +struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn, + u64 sess_id); +struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn, + unsigned long long id); + +int ksmbd_acquire_tree_conn_id(struct ksmbd_session *sess); +void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id); + +int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name); +void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id); +int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id); +#endif /* __USER_SESSION_MANAGEMENT_H__ */ diff --git a/fs/ksmbd/misc.c b/fs/ksmbd/misc.c new file mode 100644 index 0000000000000..88b39e3b400e4 --- /dev/null +++ b/fs/ksmbd/misc.c @@ -0,0 +1,488 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include + +#include "misc.h" +#include "smb_common.h" +#include "connection.h" +#include "vfs.h" + +#include "mgmt/share_config.h" + +/** + * match_pattern() - compare a string with a pattern which might include + * wildcard '*' and '?' + * TODO : implement consideration about DOS_DOT, DOS_QM and DOS_STAR + * + * @str: string to compare with a pattern + * @len: string length + * @pattern: pattern string which might include wildcard '*' and '?' + * + * Return: 0 if pattern matched with the string, otherwise non zero value + */ +int match_pattern(const char *str, size_t len, const char *pattern) +{ + const char *s = str; + const char *p = pattern; + bool star = false; + + while (*s && len) { + switch (*p) { + case '?': + s++; + len--; + p++; + break; + case '*': + star = true; + str = s; + if (!*++p) + return true; + pattern = p; + break; + default: + if (tolower(*s) == tolower(*p)) { + s++; + len--; + p++; + } else { + if (!star) + return false; + str++; + s = str; + p = pattern; + } + break; + } + } + + if (*p == '*') + ++p; + return !*p; +} + +/* + * is_char_allowed() - check for valid character + * @ch: input character to be checked + * + * Return: 1 if char is allowed, otherwise 0 + */ +static inline int is_char_allowed(char ch) +{ + /* check for control chars, wildcards etc. */ + if (!(ch & 0x80) && + (ch <= 0x1f || + ch == '?' || ch == '"' || ch == '<' || + ch == '>' || ch == '|' || ch == '*')) + return 0; + + return 1; +} + +int ksmbd_validate_filename(char *filename) +{ + while (*filename) { + char c = *filename; + + filename++; + if (!is_char_allowed(c)) { + ksmbd_debug(VFS, "File name validation failed: 0x%x\n", c); + return -ENOENT; + } + } + + return 0; +} + +static int ksmbd_validate_stream_name(char *stream_name) +{ + while (*stream_name) { + char c = *stream_name; + + stream_name++; + if (c == '/' || c == ':' || c == '\\') { + pr_err("Stream name validation failed: %c\n", c); + return -ENOENT; + } + } + + return 0; +} + +int parse_stream_name(char *filename, char **stream_name, int *s_type) +{ + char *stream_type; + char *s_name; + int rc = 0; + + s_name = filename; + filename = strsep(&s_name, ":"); + ksmbd_debug(SMB, "filename : %s, streams : %s\n", filename, s_name); + if (strchr(s_name, ':')) { + stream_type = s_name; + s_name = strsep(&stream_type, ":"); + + rc = ksmbd_validate_stream_name(s_name); + if (rc < 0) { + rc = -ENOENT; + goto out; + } + + ksmbd_debug(SMB, "stream name : %s, stream type : %s\n", s_name, + stream_type); + if (!strncasecmp("$data", stream_type, 5)) + *s_type = DATA_STREAM; + else if (!strncasecmp("$index_allocation", stream_type, 17)) + *s_type = DIR_STREAM; + else + rc = -ENOENT; + } + + *stream_name = s_name; +out: + return rc; +} + +/** + * convert_to_nt_pathname() - extract and return windows path string + * whose share directory prefix was removed from file path + * @share: ksmbd_share_config pointer + * @path: path to report + * + * Return : windows path string or error + */ + +char *convert_to_nt_pathname(struct ksmbd_share_config *share, + const struct path *path) +{ + char *pathname, *ab_pathname, *nt_pathname; + int share_path_len = share->path_sz; + + pathname = kmalloc(PATH_MAX, GFP_KERNEL); + if (!pathname) + return ERR_PTR(-EACCES); + + ab_pathname = d_path(path, pathname, PATH_MAX); + if (IS_ERR(ab_pathname)) { + nt_pathname = ERR_PTR(-EACCES); + goto free_pathname; + } + + if (strncmp(ab_pathname, share->path, share_path_len)) { + nt_pathname = ERR_PTR(-EACCES); + goto free_pathname; + } + + nt_pathname = kzalloc(strlen(&ab_pathname[share_path_len]) + 2, GFP_KERNEL); + if (!nt_pathname) { + nt_pathname = ERR_PTR(-ENOMEM); + goto free_pathname; + } + if (ab_pathname[share_path_len] == '\0') + strcpy(nt_pathname, "/"); + strcat(nt_pathname, &ab_pathname[share_path_len]); + + ksmbd_conv_path_to_windows(nt_pathname); + +free_pathname: + kfree(pathname); + return nt_pathname; +} + +int get_nlink(struct kstat *st) +{ + int nlink; + + nlink = st->nlink; + if (S_ISDIR(st->mode)) + nlink--; + + return nlink; +} + +void ksmbd_conv_path_to_unix(char *path) +{ + strreplace(path, '\\', '/'); +} + +void ksmbd_strip_last_slash(char *path) +{ + int len = strlen(path); + + while (len && path[len - 1] == '/') { + path[len - 1] = '\0'; + len--; + } +} + +void ksmbd_conv_path_to_windows(char *path) +{ + strreplace(path, '/', '\\'); +} + +char *ksmbd_casefold_sharename(struct unicode_map *um, const char *name) +{ + char *cf_name; + int cf_len; + + cf_name = kzalloc(KSMBD_REQ_MAX_SHARE_NAME, GFP_KERNEL); + if (!cf_name) + return ERR_PTR(-ENOMEM); + + if (IS_ENABLED(CONFIG_UNICODE) && um) { + const struct qstr q_name = {.name = name, .len = strlen(name)}; + + cf_len = utf8_casefold(um, &q_name, cf_name, + KSMBD_REQ_MAX_SHARE_NAME); + if (cf_len < 0) + goto out_ascii; + + return cf_name; + } + +out_ascii: + cf_len = strscpy(cf_name, name, KSMBD_REQ_MAX_SHARE_NAME); + if (cf_len < 0) { + kfree(cf_name); + return ERR_PTR(-E2BIG); + } + + for (; *cf_name; ++cf_name) + *cf_name = isascii(*cf_name) ? tolower(*cf_name) : *cf_name; + return cf_name - cf_len; +} + +/** + * ksmbd_extract_sharename() - get share name from tree connect request + * @treename: buffer containing tree name and share name + * + * Return: share name on success, otherwise error + */ +char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename) +{ + const char *name = treename, *pos = strrchr(name, '\\'); + + if (pos) + name = (pos + 1); + + /* caller has to free the memory */ + return ksmbd_casefold_sharename(um, name); +} + +/** + * convert_to_unix_name() - convert windows name to unix format + * @share: ksmbd_share_config pointer + * @name: file name that is relative to share + * + * Return: converted name on success, otherwise NULL + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name) +{ + int no_slash = 0, name_len, path_len; + char *new_name; + + if (name[0] == '/') + name++; + + path_len = share->path_sz; + name_len = strlen(name); + new_name = kmalloc(path_len + name_len + 2, GFP_KERNEL); + if (!new_name) + return new_name; + + memcpy(new_name, share->path, path_len); + if (new_name[path_len - 1] != '/') { + new_name[path_len] = '/'; + no_slash = 1; + } + + memcpy(new_name + path_len + no_slash, name, name_len); + path_len += name_len + no_slash; + new_name[path_len] = 0x00; + return new_name; +} +#else +static char *normalize_path(const char *path) +{ + size_t path_len, remain_path_len, out_path_len; + char *out_path, *out_next; + int i, pre_dotdot_cnt = 0, slash_cnt = 0; + bool is_last; + + path_len = strlen(path); + remain_path_len = path_len; + + out_path = kzalloc(path_len + 2, GFP_KERNEL); + if (!out_path) + return ERR_PTR(-ENOMEM); + out_path_len = 0; + out_next = out_path; + + do { + const char *name = path + path_len - remain_path_len; + char *next = strchrnul(name, '/'); + size_t name_len = next - name; + + is_last = !next[0]; + if (name_len == 2 && name[0] == '.' && name[1] == '.') { + pre_dotdot_cnt++; + /* handle the case that path ends with "/.." */ + if (is_last) + goto follow_dotdot; + } else { + if (pre_dotdot_cnt) { +follow_dotdot: + slash_cnt = 0; + for (i = out_path_len - 1; i >= 0; i--) { + if (out_path[i] == '/' && + ++slash_cnt == pre_dotdot_cnt + 1) + break; + } + + if (i < 0 && + slash_cnt != pre_dotdot_cnt) { + kfree(out_path); + return ERR_PTR(-EINVAL); + } + + out_next = &out_path[i+1]; + *out_next = '\0'; + out_path_len = i + 1; + + } + + if (name_len != 0 && + !(name_len == 1 && name[0] == '.') && + !(name_len == 2 && name[0] == '.' && name[1] == '.')) { + next[0] = '\0'; + sprintf(out_next, "%s/", name); + out_next += name_len + 1; + out_path_len += name_len + 1; + if (!is_last) + next[0] = '/'; + } + pre_dotdot_cnt = 0; + } + + remain_path_len -= name_len + 1; + } while (!is_last); + + if (out_path_len > 0) + out_path[out_path_len-1] = '\0'; + return out_path; +} + +char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name) +{ + int no_slash = 0, name_len, path_len; + char *new_name, *norm_name; + + if (name[0] == '/') + name++; + + norm_name = normalize_path(name); + if (IS_ERR(norm_name)) + return norm_name; + + path_len = share->path_sz; + name_len = strlen(norm_name); + new_name = kmalloc(path_len + name_len + 2, GFP_KERNEL); + if (!new_name) { + kfree(norm_name); + return new_name; + } + + memcpy(new_name, share->path, path_len); + if (new_name[path_len - 1] != '/') { + new_name[path_len] = '/'; + no_slash = 1; + } + + memcpy(new_name + path_len + no_slash, norm_name, name_len); + path_len += name_len + no_slash; + new_name[path_len] = 0x00; + kfree(norm_name); + + return new_name; +} +#endif + +char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info, + const struct nls_table *local_nls, + int *conv_len) +{ + char *conv; + int sz = min(4 * d_info->name_len, PATH_MAX); + + if (!sz) + return NULL; + + conv = kmalloc(sz, GFP_KERNEL); + if (!conv) + return NULL; + + /* XXX */ + *conv_len = smbConvertToUTF16((__le16 *)conv, d_info->name, + d_info->name_len, local_nls, 0); + *conv_len *= 2; + + /* We allocate buffer twice bigger than needed. */ + conv[*conv_len] = 0x00; + conv[*conv_len + 1] = 0x00; + return conv; +} + +/* + * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units) + * into Unix UTC (based 1970-01-01, in seconds). + */ +struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc) +{ + struct timespec64 ts; + + /* Subtract the NTFS time offset, then convert to 1s intervals. */ + s64 t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET; + u64 abs_t; + + /* + * Unfortunately can not use normal 64 bit division on 32 bit arch, but + * the alternative, do_div, does not work with negative numbers so have + * to special case them + */ + if (t < 0) { + abs_t = -t; + ts.tv_nsec = do_div(abs_t, 10000000) * 100; + ts.tv_nsec = -ts.tv_nsec; + ts.tv_sec = -abs_t; + } else { + abs_t = t; + ts.tv_nsec = do_div(abs_t, 10000000) * 100; + ts.tv_sec = abs_t; + } + + return ts; +} + +/* Convert the Unix UTC into NT UTC. */ +inline u64 ksmbd_UnixTimeToNT(struct timespec64 t) +{ + /* Convert to 100ns intervals and then add the NTFS time offset. */ + return (u64)t.tv_sec * 10000000 + t.tv_nsec / 100 + NTFS_TIME_OFFSET; +} + +inline long long ksmbd_systime(void) +{ + struct timespec64 ts; + + ktime_get_real_ts64(&ts); + return ksmbd_UnixTimeToNT(ts); +} diff --git a/fs/ksmbd/misc.h b/fs/ksmbd/misc.h new file mode 100644 index 0000000000000..1facfcd21200f --- /dev/null +++ b/fs/ksmbd/misc.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_MISC_H__ +#define __KSMBD_MISC_H__ + +struct ksmbd_share_config; +struct nls_table; +struct kstat; +struct ksmbd_file; + +int match_pattern(const char *str, size_t len, const char *pattern); +int ksmbd_validate_filename(char *filename); +int parse_stream_name(char *filename, char **stream_name, int *s_type); +char *convert_to_nt_pathname(struct ksmbd_share_config *share, + const struct path *path); +int get_nlink(struct kstat *st); +void ksmbd_conv_path_to_unix(char *path); +void ksmbd_strip_last_slash(char *path); +void ksmbd_conv_path_to_windows(char *path); +char *ksmbd_casefold_sharename(struct unicode_map *um, const char *name); +char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename); +char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name); + +#define KSMBD_DIR_INFO_ALIGNMENT 8 +struct ksmbd_dir_info; +char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info, + const struct nls_table *local_nls, + int *conv_len); + +#define NTFS_TIME_OFFSET ((u64)(369 * 365 + 89) * 24 * 3600 * 10000000) +struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc); +u64 ksmbd_UnixTimeToNT(struct timespec64 t); +long long ksmbd_systime(void); +#endif /* __KSMBD_MISC_H__ */ diff --git a/fs/ksmbd/ndr.c b/fs/ksmbd/ndr.c new file mode 100644 index 0000000000000..66685393a8102 --- /dev/null +++ b/fs/ksmbd/ndr.c @@ -0,0 +1,534 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2021 Samsung Electronics Co., Ltd. + * Author(s): Namjae Jeon + */ + +#include + +#include "glob.h" +#include "ndr.h" + +static inline char *ndr_get_field(struct ndr *n) +{ + return n->data + n->offset; +} + +static int try_to_realloc_ndr_blob(struct ndr *n, size_t sz) +{ + char *data; + + data = krealloc(n->data, n->offset + sz + 1024, GFP_KERNEL); + if (!data) + return -ENOMEM; + + n->data = data; + n->length += 1024; + memset(n->data + n->offset, 0, 1024); + return 0; +} + +static int ndr_write_int16(struct ndr *n, __u16 value) +{ + if (n->length <= n->offset + sizeof(value)) { + int ret; + + ret = try_to_realloc_ndr_blob(n, sizeof(value)); + if (ret) + return ret; + } + + *(__le16 *)ndr_get_field(n) = cpu_to_le16(value); + n->offset += sizeof(value); + return 0; +} + +static int ndr_write_int32(struct ndr *n, __u32 value) +{ + if (n->length <= n->offset + sizeof(value)) { + int ret; + + ret = try_to_realloc_ndr_blob(n, sizeof(value)); + if (ret) + return ret; + } + + *(__le32 *)ndr_get_field(n) = cpu_to_le32(value); + n->offset += sizeof(value); + return 0; +} + +static int ndr_write_int64(struct ndr *n, __u64 value) +{ + if (n->length <= n->offset + sizeof(value)) { + int ret; + + ret = try_to_realloc_ndr_blob(n, sizeof(value)); + if (ret) + return ret; + } + + *(__le64 *)ndr_get_field(n) = cpu_to_le64(value); + n->offset += sizeof(value); + return 0; +} + +static int ndr_write_bytes(struct ndr *n, void *value, size_t sz) +{ + if (n->length <= n->offset + sz) { + int ret; + + ret = try_to_realloc_ndr_blob(n, sz); + if (ret) + return ret; + } + + memcpy(ndr_get_field(n), value, sz); + n->offset += sz; + return 0; +} + +static int ndr_write_string(struct ndr *n, char *value) +{ + size_t sz; + + sz = strlen(value) + 1; + if (n->length <= n->offset + sz) { + int ret; + + ret = try_to_realloc_ndr_blob(n, sz); + if (ret) + return ret; + } + + memcpy(ndr_get_field(n), value, sz); + n->offset += sz; + n->offset = ALIGN(n->offset, 2); + return 0; +} + +static int ndr_read_string(struct ndr *n, void *value, size_t sz) +{ + int len; + + if (n->offset + sz > n->length) + return -EINVAL; + + len = strnlen(ndr_get_field(n), sz); + if (value) + memcpy(value, ndr_get_field(n), len); + len++; + n->offset += len; + n->offset = ALIGN(n->offset, 2); + return 0; +} + +static int ndr_read_bytes(struct ndr *n, void *value, size_t sz) +{ + if (n->offset + sz > n->length) + return -EINVAL; + + if (value) + memcpy(value, ndr_get_field(n), sz); + n->offset += sz; + return 0; +} + +static int ndr_read_int16(struct ndr *n, __u16 *value) +{ + if (n->offset + sizeof(__u16) > n->length) + return -EINVAL; + + if (value) + *value = le16_to_cpu(*(__le16 *)ndr_get_field(n)); + n->offset += sizeof(__u16); + return 0; +} + +static int ndr_read_int32(struct ndr *n, __u32 *value) +{ + if (n->offset + sizeof(__u32) > n->length) + return -EINVAL; + + if (value) + *value = le32_to_cpu(*(__le32 *)ndr_get_field(n)); + n->offset += sizeof(__u32); + return 0; +} + +static int ndr_read_int64(struct ndr *n, __u64 *value) +{ + if (n->offset + sizeof(__u64) > n->length) + return -EINVAL; + + if (value) + *value = le64_to_cpu(*(__le64 *)ndr_get_field(n)); + n->offset += sizeof(__u64); + return 0; +} + +int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da) +{ + char hex_attr[12] = {0}; + int ret; + + n->offset = 0; + n->length = 1024; + n->data = kzalloc(n->length, GFP_KERNEL); + if (!n->data) + return -ENOMEM; + + if (da->version == 3) { + snprintf(hex_attr, 10, "0x%x", da->attr); + ret = ndr_write_string(n, hex_attr); + } else { + ret = ndr_write_string(n, ""); + } + if (ret) + return ret; + + ret = ndr_write_int16(n, da->version); + if (ret) + return ret; + + ret = ndr_write_int32(n, da->version); + if (ret) + return ret; + + ret = ndr_write_int32(n, da->flags); + if (ret) + return ret; + + ret = ndr_write_int32(n, da->attr); + if (ret) + return ret; + + if (da->version == 3) { + ret = ndr_write_int32(n, da->ea_size); + if (ret) + return ret; + ret = ndr_write_int64(n, da->size); + if (ret) + return ret; + ret = ndr_write_int64(n, da->alloc_size); + } else { + ret = ndr_write_int64(n, da->itime); + } + if (ret) + return ret; + + ret = ndr_write_int64(n, da->create_time); + if (ret) + return ret; + + if (da->version == 3) + ret = ndr_write_int64(n, da->change_time); + return ret; +} + +int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da) +{ + char hex_attr[12]; + unsigned int version2, ret; + + n->offset = 0; + ret = ndr_read_string(n, hex_attr, sizeof(hex_attr)); + if (ret) + return ret; + + ret = ndr_read_int16(n, &da->version); + if (ret) + return ret; + + if (da->version != 3 && da->version != 4) { + ksmbd_debug(VFS, "v%d version is not supported\n", da->version); + return -EINVAL; + } + + ret = ndr_read_int32(n, &version2); + if (ret) + return ret; + + if (da->version != version2) { + ksmbd_debug(VFS, "ndr version mismatched(version: %d, version2: %d)\n", + da->version, version2); + return -EINVAL; + } + + ret = ndr_read_int32(n, NULL); + if (ret) + return ret; + + ret = ndr_read_int32(n, &da->attr); + if (ret) + return ret; + + if (da->version == 4) { + ret = ndr_read_int64(n, &da->itime); + if (ret) + return ret; + + ret = ndr_read_int64(n, &da->create_time); + } else { + ret = ndr_read_int32(n, NULL); + if (ret) + return ret; + + ret = ndr_read_int64(n, NULL); + if (ret) + return ret; + + ret = ndr_read_int64(n, NULL); + if (ret) + return ret; + + ret = ndr_read_int64(n, &da->create_time); + if (ret) + return ret; + + ret = ndr_read_int64(n, NULL); + } + + return ret; +} + +static int ndr_encode_posix_acl_entry(struct ndr *n, struct xattr_smb_acl *acl) +{ + int i, ret; + + ret = ndr_write_int32(n, acl->count); + if (ret) + return ret; + + n->offset = ALIGN(n->offset, 8); + ret = ndr_write_int32(n, acl->count); + if (ret) + return ret; + + ret = ndr_write_int32(n, 0); + if (ret) + return ret; + + for (i = 0; i < acl->count; i++) { + n->offset = ALIGN(n->offset, 8); + ret = ndr_write_int16(n, acl->entries[i].type); + if (ret) + return ret; + + ret = ndr_write_int16(n, acl->entries[i].type); + if (ret) + return ret; + + if (acl->entries[i].type == SMB_ACL_USER) { + n->offset = ALIGN(n->offset, 8); + ret = ndr_write_int64(n, acl->entries[i].uid); + } else if (acl->entries[i].type == SMB_ACL_GROUP) { + n->offset = ALIGN(n->offset, 8); + ret = ndr_write_int64(n, acl->entries[i].gid); + } + if (ret) + return ret; + + /* push permission */ + ret = ndr_write_int32(n, acl->entries[i].perm); + } + + return ret; +} + +int ndr_encode_posix_acl(struct ndr *n, + struct user_namespace *user_ns, + struct inode *inode, + struct xattr_smb_acl *acl, + struct xattr_smb_acl *def_acl) +{ + unsigned int ref_id = 0x00020000; + int ret; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + vfsuid_t vfsuid; + vfsgid_t vfsgid; +#endif + + n->offset = 0; + n->length = 1024; + n->data = kzalloc(n->length, GFP_KERNEL); + if (!n->data) + return -ENOMEM; + + if (acl) { + /* ACL ACCESS */ + ret = ndr_write_int32(n, ref_id); + ref_id += 4; + } else { + ret = ndr_write_int32(n, 0); + } + if (ret) + return ret; + + if (def_acl) { + /* DEFAULT ACL ACCESS */ + ret = ndr_write_int32(n, ref_id); + ref_id += 4; + } else { + ret = ndr_write_int32(n, 0); + } + if (ret) + return ret; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + vfsuid = i_uid_into_vfsuid(user_ns, inode); + ret = ndr_write_int64(n, from_kuid(&init_user_ns, vfsuid_into_kuid(vfsuid))); +#else + ret = ndr_write_int64(n, from_kuid(&init_user_ns, i_uid_into_mnt(user_ns, inode))); +#endif + if (ret) + return ret; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + vfsgid = i_gid_into_vfsgid(user_ns, inode); + ret = ndr_write_int64(n, from_kgid(&init_user_ns, vfsgid_into_kgid(vfsgid))); +#else + ret = ndr_write_int64(n, from_kgid(&init_user_ns, i_gid_into_mnt(user_ns, inode))); +#endif + if (ret) + return ret; +#else + ret = ndr_write_int64(n, from_kuid(&init_user_ns, inode->i_uid)); + if (ret) + return ret; + + ret = ndr_write_int64(n, from_kgid(&init_user_ns, inode->i_gid)); + if (ret) + return ret; +#endif + ret = ndr_write_int32(n, inode->i_mode); + if (ret) + return ret; + + if (acl) { + ret = ndr_encode_posix_acl_entry(n, acl); + if (def_acl && !ret) + ret = ndr_encode_posix_acl_entry(n, def_acl); + } + return ret; +} + +int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl) +{ + unsigned int ref_id = 0x00020004; + int ret; + + n->offset = 0; + n->length = 2048; + n->data = kzalloc(n->length, GFP_KERNEL); + if (!n->data) + return -ENOMEM; + + ret = ndr_write_int16(n, acl->version); + if (ret) + return ret; + + ret = ndr_write_int32(n, acl->version); + if (ret) + return ret; + + ret = ndr_write_int16(n, 2); + if (ret) + return ret; + + ret = ndr_write_int32(n, ref_id); + if (ret) + return ret; + + /* push hash type and hash 64bytes */ + ret = ndr_write_int16(n, acl->hash_type); + if (ret) + return ret; + + ret = ndr_write_bytes(n, acl->hash, XATTR_SD_HASH_SIZE); + if (ret) + return ret; + + ret = ndr_write_bytes(n, acl->desc, acl->desc_len); + if (ret) + return ret; + + ret = ndr_write_int64(n, acl->current_time); + if (ret) + return ret; + + ret = ndr_write_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE); + if (ret) + return ret; + + /* push ndr for security descriptor */ + ret = ndr_write_bytes(n, acl->sd_buf, acl->sd_size); + return ret; +} + +int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl) +{ + unsigned int version2; + int ret; + + n->offset = 0; + ret = ndr_read_int16(n, &acl->version); + if (ret) + return ret; + if (acl->version != 4) { + ksmbd_debug(VFS, "v%d version is not supported\n", acl->version); + return -EINVAL; + } + + ret = ndr_read_int32(n, &version2); + if (ret) + return ret; + if (acl->version != version2) { + ksmbd_debug(VFS, "ndr version mismatched(version: %d, version2: %d)\n", + acl->version, version2); + return -EINVAL; + } + + /* Read Level */ + ret = ndr_read_int16(n, NULL); + if (ret) + return ret; + + /* Read Ref Id */ + ret = ndr_read_int32(n, NULL); + if (ret) + return ret; + + ret = ndr_read_int16(n, &acl->hash_type); + if (ret) + return ret; + + ret = ndr_read_bytes(n, acl->hash, XATTR_SD_HASH_SIZE); + if (ret) + return ret; + + ndr_read_bytes(n, acl->desc, 10); + if (strncmp(acl->desc, "posix_acl", 9)) { + pr_err("Invalid acl description : %s\n", acl->desc); + return -EINVAL; + } + + /* Read Time */ + ret = ndr_read_int64(n, NULL); + if (ret) + return ret; + + /* Read Posix ACL hash */ + ret = ndr_read_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE); + if (ret) + return ret; + + acl->sd_size = n->length - n->offset; + acl->sd_buf = kzalloc(acl->sd_size, GFP_KERNEL); + if (!acl->sd_buf) + return -ENOMEM; + + ret = ndr_read_bytes(n, acl->sd_buf, acl->sd_size); + return ret; +} diff --git a/fs/ksmbd/ndr.h b/fs/ksmbd/ndr.h new file mode 100644 index 0000000000000..60ca265d1bb01 --- /dev/null +++ b/fs/ksmbd/ndr.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020 Samsung Electronics Co., Ltd. + * Author(s): Namjae Jeon + */ + +struct ndr { + char *data; + int offset; + int length; +}; + +#define NDR_NTSD_OFFSETOF 0xA0 + +int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da); +int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da); +int ndr_encode_posix_acl(struct ndr *n, struct user_namespace *user_ns, + struct inode *inode, struct xattr_smb_acl *acl, + struct xattr_smb_acl *def_acl); +int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl); +int ndr_encode_v3_ntacl(struct ndr *n, struct xattr_ntacl *acl); +int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl); diff --git a/fs/ksmbd/netmisc.c b/fs/ksmbd/netmisc.c new file mode 100644 index 0000000000000..155c15a946168 --- /dev/null +++ b/fs/ksmbd/netmisc.c @@ -0,0 +1,606 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) International Business Machines Corp., 2002,2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * + * Error mapping routines from Samba libsmb/errormap.c + * Copyright (C) Andrew Tridgell 2001 + */ + +#include "glob.h" +#include "smberr.h" +#include "nterr.h" +#include "smb_common.h" + +/***************************************************************************** + * convert a NT status code to a dos class/code + *****************************************************************************/ +/* NT status -> dos error map */ +static const struct { + __u8 dos_class; + __u16 dos_code; + __u32 ntstatus; +} ntstatus_to_dos_map[] = { + { + ERRDOS, ERRgeneral, NT_STATUS_UNSUCCESSFUL}, { + ERRDOS, ERRbadfunc, NT_STATUS_NOT_IMPLEMENTED}, { + ERRDOS, ERRinvlevel, NT_STATUS_INVALID_INFO_CLASS}, { + ERRDOS, 24, NT_STATUS_INFO_LENGTH_MISMATCH}, { + ERRHRD, ERRgeneral, NT_STATUS_ACCESS_VIOLATION}, { + ERRHRD, ERRgeneral, NT_STATUS_IN_PAGE_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA}, { + ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_INITIAL_STACK}, { + ERRDOS, 193, NT_STATUS_BAD_INITIAL_PC}, { + ERRDOS, 87, NT_STATUS_INVALID_CID}, { + ERRHRD, ERRgeneral, NT_STATUS_TIMER_NOT_CANCELED}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER}, { + ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_DEVICE}, { + ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_FILE}, { + ERRDOS, ERRbadfunc, NT_STATUS_INVALID_DEVICE_REQUEST}, { + ERRDOS, 38, NT_STATUS_END_OF_FILE}, { + ERRDOS, 34, NT_STATUS_WRONG_VOLUME}, { + ERRDOS, 21, NT_STATUS_NO_MEDIA_IN_DEVICE}, { + ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_MEDIA}, { + ERRDOS, 27, NT_STATUS_NONEXISTENT_SECTOR}, +/* { This NT error code was 'sqashed' + * from NT_STATUS_MORE_PROCESSING_REQUIRED to NT_STATUS_OK + * during the session setup } + */ + { + ERRDOS, ERRnomem, NT_STATUS_NO_MEMORY}, { + ERRDOS, 487, NT_STATUS_CONFLICTING_ADDRESSES}, { + ERRDOS, 487, NT_STATUS_NOT_MAPPED_VIEW}, { + ERRDOS, 87, NT_STATUS_UNABLE_TO_FREE_VM}, { + ERRDOS, 87, NT_STATUS_UNABLE_TO_DELETE_SECTION}, { + ERRDOS, 2142, NT_STATUS_INVALID_SYSTEM_SERVICE}, { + ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_INSTRUCTION}, { + ERRDOS, ERRnoaccess, NT_STATUS_INVALID_LOCK_SEQUENCE}, { + ERRDOS, ERRnoaccess, NT_STATUS_INVALID_VIEW_SIZE}, { + ERRDOS, 193, NT_STATUS_INVALID_FILE_FOR_SECTION}, { + ERRDOS, ERRnoaccess, NT_STATUS_ALREADY_COMMITTED}, +/* { This NT error code was 'sqashed' + * from NT_STATUS_ACCESS_DENIED to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE + * during the session setup } + */ + { + ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED}, { + ERRDOS, 111, NT_STATUS_BUFFER_TOO_SMALL}, { + ERRDOS, ERRbadfid, NT_STATUS_OBJECT_TYPE_MISMATCH}, { + ERRHRD, ERRgeneral, NT_STATUS_NONCONTINUABLE_EXCEPTION}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_DISPOSITION}, { + ERRHRD, ERRgeneral, NT_STATUS_UNWIND}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_STACK}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_UNWIND_TARGET}, { + ERRDOS, 158, NT_STATUS_NOT_LOCKED}, { + ERRHRD, ERRgeneral, NT_STATUS_PARITY_ERROR}, { + ERRDOS, 487, NT_STATUS_UNABLE_TO_DECOMMIT_VM}, { + ERRDOS, 487, NT_STATUS_NOT_COMMITTED}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_PORT_ATTRIBUTES}, { + ERRHRD, ERRgeneral, NT_STATUS_PORT_MESSAGE_TOO_LONG}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_MIX}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_QUOTA_LOWER}, { + ERRHRD, ERRgeneral, NT_STATUS_DISK_CORRUPT_ERROR}, { + /* mapping changed since shell does lookup on * expects FileNotFound */ + ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_INVALID}, { + ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_NOT_FOUND}, { + ERRDOS, ERRalreadyexists, NT_STATUS_OBJECT_NAME_COLLISION}, { + ERRHRD, ERRgeneral, NT_STATUS_HANDLE_NOT_WAITABLE}, { + ERRDOS, ERRbadfid, NT_STATUS_PORT_DISCONNECTED}, { + ERRHRD, ERRgeneral, NT_STATUS_DEVICE_ALREADY_ATTACHED}, { + ERRDOS, 161, NT_STATUS_OBJECT_PATH_INVALID}, { + ERRDOS, ERRbadpath, NT_STATUS_OBJECT_PATH_NOT_FOUND}, { + ERRDOS, 161, NT_STATUS_OBJECT_PATH_SYNTAX_BAD}, { + ERRHRD, ERRgeneral, NT_STATUS_DATA_OVERRUN}, { + ERRHRD, ERRgeneral, NT_STATUS_DATA_LATE_ERROR}, { + ERRDOS, 23, NT_STATUS_DATA_ERROR}, { + ERRDOS, 23, NT_STATUS_CRC_ERROR}, { + ERRDOS, ERRnomem, NT_STATUS_SECTION_TOO_BIG}, { + ERRDOS, ERRnoaccess, NT_STATUS_PORT_CONNECTION_REFUSED}, { + ERRDOS, ERRbadfid, NT_STATUS_INVALID_PORT_HANDLE}, { + ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION}, { + ERRHRD, ERRgeneral, NT_STATUS_QUOTA_EXCEEDED}, { + ERRDOS, 87, NT_STATUS_INVALID_PAGE_PROTECTION}, { + ERRDOS, 288, NT_STATUS_MUTANT_NOT_OWNED}, { + ERRDOS, 298, NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED}, { + ERRDOS, 87, NT_STATUS_PORT_ALREADY_SET}, { + ERRDOS, 87, NT_STATUS_SECTION_NOT_IMAGE}, { + ERRDOS, 156, NT_STATUS_SUSPEND_COUNT_EXCEEDED}, { + ERRDOS, ERRnoaccess, NT_STATUS_THREAD_IS_TERMINATING}, { + ERRDOS, 87, NT_STATUS_BAD_WORKING_SET_LIMIT}, { + ERRDOS, 87, NT_STATUS_INCOMPATIBLE_FILE_MAP}, { + ERRDOS, 87, NT_STATUS_SECTION_PROTECTION}, { + ERRDOS, ERReasnotsupported, NT_STATUS_EAS_NOT_SUPPORTED}, { + ERRDOS, 255, NT_STATUS_EA_TOO_LARGE}, { + ERRHRD, ERRgeneral, NT_STATUS_NONEXISTENT_EA_ENTRY}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_EAS_ON_FILE}, { + ERRHRD, ERRgeneral, NT_STATUS_EA_CORRUPT_ERROR}, { + ERRDOS, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT}, { + ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED}, { + ERRDOS, ERRbadfile, NT_STATUS_DELETE_PENDING}, { + ERRDOS, ERRunsup, NT_STATUS_CTL_FILE_NOT_SUPPORTED}, { + ERRHRD, ERRgeneral, NT_STATUS_UNKNOWN_REVISION}, { + ERRHRD, ERRgeneral, NT_STATUS_REVISION_MISMATCH}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_OWNER}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_PRIMARY_GROUP}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_IMPERSONATION_TOKEN}, { + ERRHRD, ERRgeneral, NT_STATUS_CANT_DISABLE_MANDATORY}, { + ERRDOS, 2215, NT_STATUS_NO_LOGON_SERVERS}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_LOGON_SESSION}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PRIVILEGE}, { + ERRDOS, ERRnoaccess, NT_STATUS_PRIVILEGE_NOT_HELD}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACCOUNT_NAME}, { + ERRHRD, ERRgeneral, NT_STATUS_USER_EXISTS}, +/* { This NT error code was 'sqashed' + * from NT_STATUS_NO_SUCH_USER to NT_STATUS_LOGON_FAILURE + * during the session setup } + */ + { + ERRDOS, ERRnoaccess, NT_STATUS_NO_SUCH_USER}, { /* could map to 2238 */ + ERRHRD, ERRgeneral, NT_STATUS_GROUP_EXISTS}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_GROUP}, { + ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_GROUP}, { + ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_GROUP}, { + ERRHRD, ERRgeneral, NT_STATUS_LAST_ADMIN}, +/* { This NT error code was 'sqashed' + * from NT_STATUS_WRONG_PASSWORD to NT_STATUS_LOGON_FAILURE + * during the session setup } + */ + { + ERRSRV, ERRbadpw, NT_STATUS_WRONG_PASSWORD}, { + ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_PASSWORD}, { + ERRHRD, ERRgeneral, NT_STATUS_PASSWORD_RESTRICTION}, { + ERRDOS, ERRnoaccess, NT_STATUS_LOGON_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_ACCOUNT_RESTRICTION}, { + ERRSRV, ERRbadLogonTime, NT_STATUS_INVALID_LOGON_HOURS}, { + ERRSRV, ERRbadclient, NT_STATUS_INVALID_WORKSTATION}, { + ERRSRV, ERRpasswordExpired, NT_STATUS_PASSWORD_EXPIRED}, { + ERRSRV, ERRaccountexpired, NT_STATUS_ACCOUNT_DISABLED}, { + ERRHRD, ERRgeneral, NT_STATUS_NONE_MAPPED}, { + ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_LUIDS_REQUESTED}, { + ERRHRD, ERRgeneral, NT_STATUS_LUIDS_EXHAUSTED}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_SUB_AUTHORITY}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACL}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_SID}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_SECURITY_DESCR}, { + ERRDOS, 127, NT_STATUS_PROCEDURE_NOT_FOUND}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_FORMAT}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_TOKEN}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_INHERITANCE_ACL}, { + ERRDOS, 158, NT_STATUS_RANGE_NOT_LOCKED}, { + ERRDOS, 112, NT_STATUS_DISK_FULL}, { + ERRHRD, ERRgeneral, NT_STATUS_SERVER_DISABLED}, { + ERRHRD, ERRgeneral, NT_STATUS_SERVER_NOT_DISABLED}, { + ERRDOS, 68, NT_STATUS_TOO_MANY_GUIDS_REQUESTED}, { + ERRDOS, 259, NT_STATUS_GUIDS_EXHAUSTED}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_ID_AUTHORITY}, { + ERRDOS, 259, NT_STATUS_AGENTS_EXHAUSTED}, { + ERRDOS, 154, NT_STATUS_INVALID_VOLUME_LABEL}, { + ERRDOS, 14, NT_STATUS_SECTION_NOT_EXTENDED}, { + ERRDOS, 487, NT_STATUS_NOT_MAPPED_DATA}, { + ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_DATA_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_TYPE_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_NAME_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_ARRAY_BOUNDS_EXCEEDED}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DENORMAL_OPERAND}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DIVIDE_BY_ZERO}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INEXACT_RESULT}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INVALID_OPERATION}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_OVERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_STACK_CHECK}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOAT_UNDERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_INTEGER_DIVIDE_BY_ZERO}, { + ERRDOS, 534, NT_STATUS_INTEGER_OVERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_PRIVILEGED_INSTRUCTION}, { + ERRDOS, ERRnomem, NT_STATUS_TOO_MANY_PAGING_FILES}, { + ERRHRD, ERRgeneral, NT_STATUS_FILE_INVALID}, { + ERRHRD, ERRgeneral, NT_STATUS_ALLOTTED_SPACE_EXCEEDED}, +/* { This NT error code was 'sqashed' + * from NT_STATUS_INSUFFICIENT_RESOURCES to + * NT_STATUS_INSUFF_SERVER_RESOURCES during the session setup } + */ + { + ERRDOS, ERRnoresource, NT_STATUS_INSUFFICIENT_RESOURCES}, { + ERRDOS, ERRbadpath, NT_STATUS_DFS_EXIT_PATH_FOUND}, { + ERRDOS, 23, NT_STATUS_DEVICE_DATA_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_CONNECTED}, { + ERRDOS, 21, NT_STATUS_DEVICE_POWER_FAILURE}, { + ERRDOS, 487, NT_STATUS_FREE_VM_NOT_AT_BASE}, { + ERRDOS, 487, NT_STATUS_MEMORY_NOT_ALLOCATED}, { + ERRHRD, ERRgeneral, NT_STATUS_WORKING_SET_QUOTA}, { + ERRDOS, 19, NT_STATUS_MEDIA_WRITE_PROTECTED}, { + ERRDOS, 21, NT_STATUS_DEVICE_NOT_READY}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_GROUP_ATTRIBUTES}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_IMPERSONATION_LEVEL}, { + ERRHRD, ERRgeneral, NT_STATUS_CANT_OPEN_ANONYMOUS}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_VALIDATION_CLASS}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_TOKEN_TYPE}, { + ERRDOS, 87, NT_STATUS_BAD_MASTER_BOOT_RECORD}, { + ERRHRD, ERRgeneral, NT_STATUS_INSTRUCTION_MISALIGNMENT}, { + ERRDOS, ERRpipebusy, NT_STATUS_INSTANCE_NOT_AVAILABLE}, { + ERRDOS, ERRpipebusy, NT_STATUS_PIPE_NOT_AVAILABLE}, { + ERRDOS, ERRbadpipe, NT_STATUS_INVALID_PIPE_STATE}, { + ERRDOS, ERRpipebusy, NT_STATUS_PIPE_BUSY}, { + ERRDOS, ERRbadfunc, NT_STATUS_ILLEGAL_FUNCTION}, { + ERRDOS, ERRnotconnected, NT_STATUS_PIPE_DISCONNECTED}, { + ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_CLOSING}, { + ERRHRD, ERRgeneral, NT_STATUS_PIPE_CONNECTED}, { + ERRHRD, ERRgeneral, NT_STATUS_PIPE_LISTENING}, { + ERRDOS, ERRbadpipe, NT_STATUS_INVALID_READ_MODE}, { + ERRDOS, 121, NT_STATUS_IO_TIMEOUT}, { + ERRDOS, 38, NT_STATUS_FILE_FORCED_CLOSED}, { + ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STARTED}, { + ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STOPPED}, { + ERRHRD, ERRgeneral, NT_STATUS_COULD_NOT_INTERPRET}, { + ERRDOS, ERRnoaccess, NT_STATUS_FILE_IS_A_DIRECTORY}, { + ERRDOS, ERRunsup, NT_STATUS_NOT_SUPPORTED}, { + ERRDOS, 51, NT_STATUS_REMOTE_NOT_LISTENING}, { + ERRDOS, 52, NT_STATUS_DUPLICATE_NAME}, { + ERRDOS, 53, NT_STATUS_BAD_NETWORK_PATH}, { + ERRDOS, 54, NT_STATUS_NETWORK_BUSY}, { + ERRDOS, 55, NT_STATUS_DEVICE_DOES_NOT_EXIST}, { + ERRDOS, 56, NT_STATUS_TOO_MANY_COMMANDS}, { + ERRDOS, 57, NT_STATUS_ADAPTER_HARDWARE_ERROR}, { + ERRDOS, 58, NT_STATUS_INVALID_NETWORK_RESPONSE}, { + ERRDOS, 59, NT_STATUS_UNEXPECTED_NETWORK_ERROR}, { + ERRDOS, 60, NT_STATUS_BAD_REMOTE_ADAPTER}, { + ERRDOS, 61, NT_STATUS_PRINT_QUEUE_FULL}, { + ERRDOS, 62, NT_STATUS_NO_SPOOL_SPACE}, { + ERRDOS, 63, NT_STATUS_PRINT_CANCELLED}, { + ERRDOS, 64, NT_STATUS_NETWORK_NAME_DELETED}, { + ERRDOS, 65, NT_STATUS_NETWORK_ACCESS_DENIED}, { + ERRDOS, 66, NT_STATUS_BAD_DEVICE_TYPE}, { + ERRDOS, ERRnosuchshare, NT_STATUS_BAD_NETWORK_NAME}, { + ERRDOS, 68, NT_STATUS_TOO_MANY_NAMES}, { + ERRDOS, 69, NT_STATUS_TOO_MANY_SESSIONS}, { + ERRDOS, 70, NT_STATUS_SHARING_PAUSED}, { + ERRDOS, 71, NT_STATUS_REQUEST_NOT_ACCEPTED}, { + ERRDOS, 72, NT_STATUS_REDIRECTOR_PAUSED}, { + ERRDOS, 88, NT_STATUS_NET_WRITE_FAULT}, { + ERRHRD, ERRgeneral, NT_STATUS_PROFILING_AT_LIMIT}, { + ERRDOS, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE}, { + ERRDOS, ERRnoaccess, NT_STATUS_FILE_RENAMED}, { + ERRDOS, 240, NT_STATUS_VIRTUAL_CIRCUIT_CLOSED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SECURITY_ON_OBJECT}, { + ERRHRD, ERRgeneral, NT_STATUS_CANT_WAIT}, { + ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_EMPTY}, { + ERRHRD, ERRgeneral, NT_STATUS_CANT_ACCESS_DOMAIN_INFO}, { + ERRHRD, ERRgeneral, NT_STATUS_CANT_TERMINATE_SELF}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_SERVER_STATE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_STATE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_ROLE}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_DOMAIN}, { + ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_EXISTS}, { + ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_LIMIT_EXCEEDED}, { + ERRDOS, 300, NT_STATUS_OPLOCK_NOT_GRANTED}, { + ERRDOS, 301, NT_STATUS_INVALID_OPLOCK_PROTOCOL}, { + ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_CORRUPTION}, { + ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_GENERIC_NOT_MAPPED}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_DESCRIPTOR_FORMAT}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_USER_BUFFER}, { + ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_IO_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_CREATE_ERR}, { + ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_MAP_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_EXTEND_ERR}, { + ERRHRD, ERRgeneral, NT_STATUS_NOT_LOGON_PROCESS}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_EXISTS}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_1}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_2}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_3}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_4}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_5}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_6}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_7}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_8}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_9}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_10}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_11}, { + ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_12}, { + ERRDOS, ERRbadpath, NT_STATUS_REDIRECTOR_NOT_STARTED}, { + ERRHRD, ERRgeneral, NT_STATUS_REDIRECTOR_STARTED}, { + ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PACKAGE}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_FUNCTION_TABLE}, { + ERRDOS, 203, 0xc0000100}, { + ERRDOS, 145, NT_STATUS_DIRECTORY_NOT_EMPTY}, { + ERRHRD, ERRgeneral, NT_STATUS_FILE_CORRUPT_ERROR}, { + ERRDOS, 267, NT_STATUS_NOT_A_DIRECTORY}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_LOGON_SESSION_STATE}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_COLLISION}, { + ERRDOS, 206, NT_STATUS_NAME_TOO_LONG}, { + ERRDOS, 2401, NT_STATUS_FILES_OPEN}, { + ERRDOS, 2404, NT_STATUS_CONNECTION_IN_USE}, { + ERRHRD, ERRgeneral, NT_STATUS_MESSAGE_NOT_FOUND}, { + ERRDOS, ERRnoaccess, NT_STATUS_PROCESS_IS_TERMINATING}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_LOGON_TYPE}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_GUID_TRANSLATION}, { + ERRHRD, ERRgeneral, NT_STATUS_CANNOT_IMPERSONATE}, { + ERRHRD, ERRgeneral, NT_STATUS_IMAGE_ALREADY_LOADED}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_PRESENT}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_NOT_EXIST}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_ALREADY_OWNED}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_LID_OWNER}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_COMMAND}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_LID}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_SELECTOR}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_LDT}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_SIZE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_OFFSET}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_DESCRIPTOR}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NE_FORMAT}, { + ERRHRD, ERRgeneral, NT_STATUS_RXACT_INVALID_STATE}, { + ERRHRD, ERRgeneral, NT_STATUS_RXACT_COMMIT_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_MAPPED_FILE_SIZE_ZERO}, { + ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES}, { + ERRHRD, ERRgeneral, NT_STATUS_CANCELLED}, { + ERRDOS, ERRnoaccess, NT_STATUS_CANNOT_DELETE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_COMPUTER_NAME}, { + ERRDOS, ERRnoaccess, NT_STATUS_FILE_DELETED}, { + ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_ACCOUNT}, { + ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_GROUP}, { + ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_USER}, { + ERRHRD, ERRgeneral, NT_STATUS_MEMBERS_PRIMARY_GROUP}, { + ERRDOS, ERRbadfid, NT_STATUS_FILE_CLOSED}, { + ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_THREADS}, { + ERRHRD, ERRgeneral, NT_STATUS_THREAD_NOT_IN_PROCESS}, { + ERRHRD, ERRgeneral, NT_STATUS_TOKEN_ALREADY_IN_USE}, { + ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA_EXCEEDED}, { + ERRHRD, ERRgeneral, NT_STATUS_COMMITMENT_LIMIT}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_LE_FORMAT}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NOT_MZ}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_PROTECT}, { + ERRDOS, 193, NT_STATUS_INVALID_IMAGE_WIN_16}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGON_SERVER_CONFLICT}, { + ERRHRD, ERRgeneral, NT_STATUS_TIME_DIFFERENCE_AT_DC}, { + ERRHRD, ERRgeneral, NT_STATUS_SYNCHRONIZATION_REQUIRED}, { + ERRDOS, 126, NT_STATUS_DLL_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_OPEN_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_IO_PRIVILEGE_FAILED}, { + ERRDOS, 182, NT_STATUS_ORDINAL_NOT_FOUND}, { + ERRDOS, 127, NT_STATUS_ENTRYPOINT_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_CONTROL_C_EXIT}, { + ERRDOS, 64, NT_STATUS_LOCAL_DISCONNECT}, { + ERRDOS, 64, NT_STATUS_REMOTE_DISCONNECT}, { + ERRDOS, 51, NT_STATUS_REMOTE_RESOURCES}, { + ERRDOS, 59, NT_STATUS_LINK_FAILED}, { + ERRDOS, 59, NT_STATUS_LINK_TIMEOUT}, { + ERRDOS, 59, NT_STATUS_INVALID_CONNECTION}, { + ERRDOS, 59, NT_STATUS_INVALID_ADDRESS}, { + ERRHRD, ERRgeneral, NT_STATUS_DLL_INIT_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_MISSING_SYSTEMFILE}, { + ERRHRD, ERRgeneral, NT_STATUS_UNHANDLED_EXCEPTION}, { + ERRHRD, ERRgeneral, NT_STATUS_APP_INIT_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_CREATE_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_PAGEFILE}, { + ERRDOS, 124, NT_STATUS_INVALID_LEVEL}, { + ERRDOS, 86, NT_STATUS_WRONG_PASSWORD_CORE}, { + ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_FLOAT_CONTEXT}, { + ERRDOS, 109, NT_STATUS_PIPE_BROKEN}, { + ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_CORRUPT}, { + ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_IO_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_EVENT_PAIR}, { + ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_VOLUME}, { + ERRHRD, ERRgeneral, NT_STATUS_SERIAL_NO_DEVICE_INITED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_ALIAS}, { + ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_ALIAS}, { + ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_ALIAS}, { + ERRHRD, ERRgeneral, NT_STATUS_ALIAS_EXISTS}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGON_NOT_GRANTED}, { + ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SECRETS}, { + ERRHRD, ERRgeneral, NT_STATUS_SECRET_TOO_LONG}, { + ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_FULLSCREEN_MODE}, { + ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_CONTEXT_IDS}, { + ERRDOS, ERRnoaccess, NT_STATUS_LOGON_TYPE_NOT_GRANTED}, { + ERRHRD, ERRgeneral, NT_STATUS_NOT_REGISTRY_FILE}, { + ERRHRD, ERRgeneral, NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED}, { + ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_FT_MISSING_MEMBER}, { + ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_SERVICE_ENTRY}, { + ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_CHARACTER}, { + ERRHRD, ERRgeneral, NT_STATUS_UNMAPPABLE_CHARACTER}, { + ERRHRD, ERRgeneral, NT_STATUS_UNDEFINED_CHARACTER}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_VOLUME}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_WRONG_CYLINDER}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_UNKNOWN_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_BAD_REGISTERS}, { + ERRHRD, ERRgeneral, NT_STATUS_DISK_RECALIBRATE_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_DISK_OPERATION_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_DISK_RESET_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_SHARED_IRQ_BUSY}, { + ERRHRD, ERRgeneral, NT_STATUS_FT_ORPHANING}, { + ERRHRD, ERRgeneral, 0xc000016e}, { + ERRHRD, ERRgeneral, 0xc000016f}, { + ERRHRD, ERRgeneral, 0xc0000170}, { + ERRHRD, ERRgeneral, 0xc0000171}, { + ERRHRD, ERRgeneral, NT_STATUS_PARTITION_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_BLOCK_LENGTH}, { + ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_PARTITIONED}, { + ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_LOCK_MEDIA}, { + ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_UNLOAD_MEDIA}, { + ERRHRD, ERRgeneral, NT_STATUS_EOM_OVERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_MEDIA}, { + ERRHRD, ERRgeneral, 0xc0000179}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_MEMBER}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_MEMBER}, { + ERRHRD, ERRgeneral, NT_STATUS_KEY_DELETED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_LOG_SPACE}, { + ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SIDS}, { + ERRHRD, ERRgeneral, NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED}, { + ERRHRD, ERRgeneral, NT_STATUS_KEY_HAS_CHILDREN}, { + ERRHRD, ERRgeneral, NT_STATUS_CHILD_MUST_BE_VOLATILE}, { + ERRDOS, 87, NT_STATUS_DEVICE_CONFIGURATION_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_DRIVER_INTERNAL_ERROR}, { + ERRDOS, 22, NT_STATUS_INVALID_DEVICE_STATE}, { + ERRHRD, ERRgeneral, NT_STATUS_IO_DEVICE_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_DEVICE_PROTOCOL_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_BACKUP_CONTROLLER}, { + ERRHRD, ERRgeneral, NT_STATUS_LOG_FILE_FULL}, { + ERRDOS, 19, NT_STATUS_TOO_LATE}, { + ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_LSA_SECRET}, +/* { This NT error code was 'sqashed' + * from NT_STATUS_NO_TRUST_SAM_ACCOUNT to + * NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE during the session setup } + */ + { + ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_SAM_ACCOUNT}, { + ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_DOMAIN_FAILURE}, { + ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CORRUPT}, { + ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_CANT_START}, { + ERRDOS, ERRnoaccess, NT_STATUS_TRUST_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_MUTANT_LIMIT_EXCEEDED}, { + ERRDOS, ERRnetlogonNotStarted, NT_STATUS_NETLOGON_NOT_STARTED}, { + ERRSRV, ERRaccountexpired, NT_STATUS_ACCOUNT_EXPIRED}, { + ERRHRD, ERRgeneral, NT_STATUS_POSSIBLE_DEADLOCK}, { + ERRHRD, ERRgeneral, NT_STATUS_NETWORK_CREDENTIAL_CONFLICT}, { + ERRHRD, ERRgeneral, NT_STATUS_REMOTE_SESSION_LIMIT}, { + ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CHANGED}, { + ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT}, { + ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT}, { + ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT}, +/* { This NT error code was 'sqashed' + * from NT_STATUS_DOMAIN_TRUST_INCONSISTENT to NT_STATUS_LOGON_FAILURE + * during the session setup } + */ + { + ERRDOS, ERRnoaccess, NT_STATUS_DOMAIN_TRUST_INCONSISTENT}, { + ERRHRD, ERRgeneral, NT_STATUS_FS_DRIVER_REQUIRED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_USER_SESSION_KEY}, { + ERRDOS, 59, NT_STATUS_USER_SESSION_DELETED}, { + ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_LANG_NOT_FOUND}, { + ERRDOS, ERRnoresource, NT_STATUS_INSUFF_SERVER_RESOURCES}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_BUFFER_SIZE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_COMPONENT}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_WILDCARD}, { + ERRDOS, 68, NT_STATUS_TOO_MANY_ADDRESSES}, { + ERRDOS, 52, NT_STATUS_ADDRESS_ALREADY_EXISTS}, { + ERRDOS, 64, NT_STATUS_ADDRESS_CLOSED}, { + ERRDOS, 64, NT_STATUS_CONNECTION_DISCONNECTED}, { + ERRDOS, 64, NT_STATUS_CONNECTION_RESET}, { + ERRDOS, 68, NT_STATUS_TOO_MANY_NODES}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_ABORTED}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_TIMED_OUT}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_NO_RELEASE}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_NO_MATCH}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_RESPONDED}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_ID}, { + ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_TYPE}, { + ERRDOS, ERRunsup, NT_STATUS_NOT_SERVER_SESSION}, { + ERRDOS, ERRunsup, NT_STATUS_NOT_CLIENT_SESSION}, { + ERRHRD, ERRgeneral, NT_STATUS_CANNOT_LOAD_REGISTRY_FILE}, { + ERRHRD, ERRgeneral, NT_STATUS_DEBUG_ATTACH_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_SYSTEM_PROCESS_TERMINATED}, { + ERRHRD, ERRgeneral, NT_STATUS_DATA_NOT_ACCEPTED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_BROWSER_SERVERS_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_VDM_HARD_ERROR}, { + ERRHRD, ERRgeneral, NT_STATUS_DRIVER_CANCEL_TIMEOUT}, { + ERRHRD, ERRgeneral, NT_STATUS_REPLY_MESSAGE_MISMATCH}, { + ERRHRD, ERRgeneral, NT_STATUS_MAPPED_ALIGNMENT}, { + ERRDOS, 193, NT_STATUS_IMAGE_CHECKSUM_MISMATCH}, { + ERRHRD, ERRgeneral, NT_STATUS_LOST_WRITEBEHIND_DATA}, { + ERRHRD, ERRgeneral, NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID}, { + ERRSRV, ERRpasswordExpired, NT_STATUS_PASSWORD_MUST_CHANGE}, { + ERRHRD, ERRgeneral, NT_STATUS_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_NOT_TINY_STREAM}, { + ERRHRD, ERRgeneral, NT_STATUS_RECOVERY_FAILURE}, { + ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW_READ}, { + ERRHRD, ERRgeneral, NT_STATUS_FAIL_CHECK}, { + ERRHRD, ERRgeneral, NT_STATUS_DUPLICATE_OBJECTID}, { + ERRHRD, ERRgeneral, NT_STATUS_OBJECTID_EXISTS}, { + ERRHRD, ERRgeneral, NT_STATUS_CONVERT_TO_LARGE}, { + ERRHRD, ERRgeneral, NT_STATUS_RETRY}, { + ERRHRD, ERRgeneral, NT_STATUS_FOUND_OUT_OF_SCOPE}, { + ERRHRD, ERRgeneral, NT_STATUS_ALLOCATE_BUCKET}, { + ERRHRD, ERRgeneral, NT_STATUS_PROPSET_NOT_FOUND}, { + ERRHRD, ERRgeneral, NT_STATUS_MARSHALL_OVERFLOW}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_VARIANT}, { + ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND}, { + ERRDOS, ERRnoaccess, NT_STATUS_ACCOUNT_LOCKED_OUT}, { + ERRDOS, ERRbadfid, NT_STATUS_HANDLE_NOT_CLOSABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_REFUSED}, { + ERRHRD, ERRgeneral, NT_STATUS_GRACEFUL_DISCONNECT}, { + ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_ALREADY_ASSOCIATED}, { + ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_NOT_ASSOCIATED}, { + ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_INVALID}, { + ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ACTIVE}, { + ERRHRD, ERRgeneral, NT_STATUS_NETWORK_UNREACHABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_HOST_UNREACHABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_PROTOCOL_UNREACHABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_PORT_UNREACHABLE}, { + ERRHRD, ERRgeneral, NT_STATUS_REQUEST_ABORTED}, { + ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ABORTED}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_COMPRESSION_BUFFER}, { + ERRHRD, ERRgeneral, NT_STATUS_USER_MAPPED_FILE}, { + ERRHRD, ERRgeneral, NT_STATUS_AUDIT_FAILED}, { + ERRHRD, ERRgeneral, NT_STATUS_TIMER_RESOLUTION_NOT_SET}, { + ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_COUNT_LIMIT}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGIN_TIME_RESTRICTION}, { + ERRHRD, ERRgeneral, NT_STATUS_LOGIN_WKSTA_RESTRICTION}, { + ERRDOS, 193, NT_STATUS_IMAGE_MP_UP_MISMATCH}, { + ERRHRD, ERRgeneral, 0xc000024a}, { + ERRHRD, ERRgeneral, 0xc000024b}, { + ERRHRD, ERRgeneral, 0xc000024c}, { + ERRHRD, ERRgeneral, 0xc000024d}, { + ERRHRD, ERRgeneral, 0xc000024e}, { + ERRHRD, ERRgeneral, 0xc000024f}, { + ERRHRD, ERRgeneral, NT_STATUS_INSUFFICIENT_LOGON_INFO}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_DLL_ENTRYPOINT}, { + ERRHRD, ERRgeneral, NT_STATUS_BAD_SERVICE_ENTRYPOINT}, { + ERRHRD, ERRgeneral, NT_STATUS_LPC_REPLY_LOST}, { + ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT1}, { + ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT2}, { + ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_QUOTA_LIMIT}, { + ERRSRV, 3, NT_STATUS_PATH_NOT_COVERED}, { + ERRHRD, ERRgeneral, NT_STATUS_NO_CALLBACK_ACTIVE}, { + ERRHRD, ERRgeneral, NT_STATUS_LICENSE_QUOTA_EXCEEDED}, { + ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_SHORT}, { + ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_RECENT}, { + ERRHRD, ERRgeneral, NT_STATUS_PWD_HISTORY_CONFLICT}, { + ERRHRD, ERRgeneral, 0xc000025d}, { + ERRHRD, ERRgeneral, NT_STATUS_PLUGPLAY_NO_DEVICE}, { + ERRHRD, ERRgeneral, NT_STATUS_UNSUPPORTED_COMPRESSION}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_HW_PROFILE}, { + ERRHRD, ERRgeneral, NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH}, { + ERRDOS, 182, NT_STATUS_DRIVER_ORDINAL_NOT_FOUND}, { + ERRDOS, 127, NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND}, { + ERRDOS, 288, NT_STATUS_RESOURCE_NOT_OWNED}, { + ERRDOS, ErrTooManyLinks, NT_STATUS_TOO_MANY_LINKS}, { + ERRHRD, ERRgeneral, NT_STATUS_QUOTA_LIST_INCONSISTENT}, { + ERRHRD, ERRgeneral, NT_STATUS_FILE_IS_OFFLINE}, { + ERRDOS, 21, 0xc000026e}, { + ERRDOS, 161, 0xc0000281}, { + ERRDOS, ERRnoaccess, 0xc000028a}, { + ERRDOS, ERRnoaccess, 0xc000028b}, { + ERRHRD, ERRgeneral, 0xc000028c}, { + ERRDOS, ERRnoaccess, 0xc000028d}, { + ERRDOS, ERRnoaccess, 0xc000028e}, { + ERRDOS, ERRnoaccess, 0xc000028f}, { + ERRDOS, ERRnoaccess, 0xc0000290}, { + ERRDOS, ERRbadfunc, 0xc000029c}, { + ERRDOS, ERRsymlink, NT_STATUS_STOPPED_ON_SYMLINK}, { + ERRDOS, ERRinvlevel, 0x007c0001}, }; + +void +ntstatus_to_dos(__le32 ntstatus, __u8 *eclass, __le16 *ecode) +{ + int i; + + if (ntstatus == 0) { + *eclass = 0; + *ecode = 0; + return; + } + for (i = 0; ntstatus_to_dos_map[i].ntstatus; i++) { + if (le32_to_cpu(ntstatus) == ntstatus_to_dos_map[i].ntstatus) { + *eclass = ntstatus_to_dos_map[i].dos_class; + *ecode = cpu_to_le16(ntstatus_to_dos_map[i].dos_code); + return; + } + } + *eclass = ERRHRD; + *ecode = cpu_to_le16(ERRgeneral); +} diff --git a/fs/ksmbd/nterr.h b/fs/ksmbd/nterr.h new file mode 100644 index 0000000000000..2f358f88a0188 --- /dev/null +++ b/fs/ksmbd/nterr.h @@ -0,0 +1,543 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Unix SMB/Netbios implementation. + * Version 1.9. + * NT error code constants + * Copyright (C) Andrew Tridgell 1992-2000 + * Copyright (C) John H Terpstra 1996-2000 + * Copyright (C) Luke Kenneth Casson Leighton 1996-2000 + * Copyright (C) Paul Ashton 1998-2000 + */ + +#ifndef _NTERR_H +#define _NTERR_H + +/* Win32 Status codes. */ +#define NT_STATUS_MORE_ENTRIES 0x0105 +#define NT_ERROR_INVALID_PARAMETER 0x0057 +#define NT_ERROR_INSUFFICIENT_BUFFER 0x007a +#define NT_STATUS_1804 0x070c +#define NT_STATUS_NOTIFY_ENUM_DIR 0x010c +#define NT_STATUS_INVALID_LOCK_RANGE (0xC0000000 | 0x01a1) +/* + * Win32 Error codes extracted using a loop in smbclient then printing a netmon + * sniff to a file. + */ + +#define NT_STATUS_OK 0x0000 +#define NT_STATUS_SOME_UNMAPPED 0x0107 +#define NT_STATUS_BUFFER_OVERFLOW 0x80000005 +#define NT_STATUS_NO_MORE_ENTRIES 0x8000001a +#define NT_STATUS_MEDIA_CHANGED 0x8000001c +#define NT_STATUS_END_OF_MEDIA 0x8000001e +#define NT_STATUS_MEDIA_CHECK 0x80000020 +#define NT_STATUS_NO_DATA_DETECTED 0x8000001c +#define NT_STATUS_STOPPED_ON_SYMLINK 0x8000002d +#define NT_STATUS_DEVICE_REQUIRES_CLEANING 0x80000288 +#define NT_STATUS_DEVICE_DOOR_OPEN 0x80000288 +#define NT_STATUS_UNSUCCESSFUL (0xC0000000 | 0x0001) +#define NT_STATUS_NOT_IMPLEMENTED (0xC0000000 | 0x0002) +#define NT_STATUS_INVALID_INFO_CLASS (0xC0000000 | 0x0003) +#define NT_STATUS_INFO_LENGTH_MISMATCH (0xC0000000 | 0x0004) +#define NT_STATUS_ACCESS_VIOLATION (0xC0000000 | 0x0005) +#define NT_STATUS_IN_PAGE_ERROR (0xC0000000 | 0x0006) +#define NT_STATUS_PAGEFILE_QUOTA (0xC0000000 | 0x0007) +#define NT_STATUS_INVALID_HANDLE (0xC0000000 | 0x0008) +#define NT_STATUS_BAD_INITIAL_STACK (0xC0000000 | 0x0009) +#define NT_STATUS_BAD_INITIAL_PC (0xC0000000 | 0x000a) +#define NT_STATUS_INVALID_CID (0xC0000000 | 0x000b) +#define NT_STATUS_TIMER_NOT_CANCELED (0xC0000000 | 0x000c) +#define NT_STATUS_INVALID_PARAMETER (0xC0000000 | 0x000d) +#define NT_STATUS_NO_SUCH_DEVICE (0xC0000000 | 0x000e) +#define NT_STATUS_NO_SUCH_FILE (0xC0000000 | 0x000f) +#define NT_STATUS_INVALID_DEVICE_REQUEST (0xC0000000 | 0x0010) +#define NT_STATUS_END_OF_FILE (0xC0000000 | 0x0011) +#define NT_STATUS_WRONG_VOLUME (0xC0000000 | 0x0012) +#define NT_STATUS_NO_MEDIA_IN_DEVICE (0xC0000000 | 0x0013) +#define NT_STATUS_UNRECOGNIZED_MEDIA (0xC0000000 | 0x0014) +#define NT_STATUS_NONEXISTENT_SECTOR (0xC0000000 | 0x0015) +#define NT_STATUS_MORE_PROCESSING_REQUIRED (0xC0000000 | 0x0016) +#define NT_STATUS_NO_MEMORY (0xC0000000 | 0x0017) +#define NT_STATUS_CONFLICTING_ADDRESSES (0xC0000000 | 0x0018) +#define NT_STATUS_NOT_MAPPED_VIEW (0xC0000000 | 0x0019) +#define NT_STATUS_UNABLE_TO_FREE_VM (0x80000000 | 0x001a) +#define NT_STATUS_UNABLE_TO_DELETE_SECTION (0xC0000000 | 0x001b) +#define NT_STATUS_INVALID_SYSTEM_SERVICE (0xC0000000 | 0x001c) +#define NT_STATUS_ILLEGAL_INSTRUCTION (0xC0000000 | 0x001d) +#define NT_STATUS_INVALID_LOCK_SEQUENCE (0xC0000000 | 0x001e) +#define NT_STATUS_INVALID_VIEW_SIZE (0xC0000000 | 0x001f) +#define NT_STATUS_INVALID_FILE_FOR_SECTION (0xC0000000 | 0x0020) +#define NT_STATUS_ALREADY_COMMITTED (0xC0000000 | 0x0021) +#define NT_STATUS_ACCESS_DENIED (0xC0000000 | 0x0022) +#define NT_STATUS_BUFFER_TOO_SMALL (0xC0000000 | 0x0023) +#define NT_STATUS_OBJECT_TYPE_MISMATCH (0xC0000000 | 0x0024) +#define NT_STATUS_NONCONTINUABLE_EXCEPTION (0xC0000000 | 0x0025) +#define NT_STATUS_INVALID_DISPOSITION (0xC0000000 | 0x0026) +#define NT_STATUS_UNWIND (0xC0000000 | 0x0027) +#define NT_STATUS_BAD_STACK (0xC0000000 | 0x0028) +#define NT_STATUS_INVALID_UNWIND_TARGET (0xC0000000 | 0x0029) +#define NT_STATUS_NOT_LOCKED (0xC0000000 | 0x002a) +#define NT_STATUS_PARITY_ERROR (0xC0000000 | 0x002b) +#define NT_STATUS_UNABLE_TO_DECOMMIT_VM (0xC0000000 | 0x002c) +#define NT_STATUS_NOT_COMMITTED (0xC0000000 | 0x002d) +#define NT_STATUS_INVALID_PORT_ATTRIBUTES (0xC0000000 | 0x002e) +#define NT_STATUS_PORT_MESSAGE_TOO_LONG (0xC0000000 | 0x002f) +#define NT_STATUS_INVALID_PARAMETER_MIX (0xC0000000 | 0x0030) +#define NT_STATUS_INVALID_QUOTA_LOWER (0xC0000000 | 0x0031) +#define NT_STATUS_DISK_CORRUPT_ERROR (0xC0000000 | 0x0032) +#define NT_STATUS_OBJECT_NAME_INVALID (0xC0000000 | 0x0033) +#define NT_STATUS_OBJECT_NAME_NOT_FOUND (0xC0000000 | 0x0034) +#define NT_STATUS_OBJECT_NAME_COLLISION (0xC0000000 | 0x0035) +#define NT_STATUS_HANDLE_NOT_WAITABLE (0xC0000000 | 0x0036) +#define NT_STATUS_PORT_DISCONNECTED (0xC0000000 | 0x0037) +#define NT_STATUS_DEVICE_ALREADY_ATTACHED (0xC0000000 | 0x0038) +#define NT_STATUS_OBJECT_PATH_INVALID (0xC0000000 | 0x0039) +#define NT_STATUS_OBJECT_PATH_NOT_FOUND (0xC0000000 | 0x003a) +#define NT_STATUS_OBJECT_PATH_SYNTAX_BAD (0xC0000000 | 0x003b) +#define NT_STATUS_DATA_OVERRUN (0xC0000000 | 0x003c) +#define NT_STATUS_DATA_LATE_ERROR (0xC0000000 | 0x003d) +#define NT_STATUS_DATA_ERROR (0xC0000000 | 0x003e) +#define NT_STATUS_CRC_ERROR (0xC0000000 | 0x003f) +#define NT_STATUS_SECTION_TOO_BIG (0xC0000000 | 0x0040) +#define NT_STATUS_PORT_CONNECTION_REFUSED (0xC0000000 | 0x0041) +#define NT_STATUS_INVALID_PORT_HANDLE (0xC0000000 | 0x0042) +#define NT_STATUS_SHARING_VIOLATION (0xC0000000 | 0x0043) +#define NT_STATUS_QUOTA_EXCEEDED (0xC0000000 | 0x0044) +#define NT_STATUS_INVALID_PAGE_PROTECTION (0xC0000000 | 0x0045) +#define NT_STATUS_MUTANT_NOT_OWNED (0xC0000000 | 0x0046) +#define NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED (0xC0000000 | 0x0047) +#define NT_STATUS_PORT_ALREADY_SET (0xC0000000 | 0x0048) +#define NT_STATUS_SECTION_NOT_IMAGE (0xC0000000 | 0x0049) +#define NT_STATUS_SUSPEND_COUNT_EXCEEDED (0xC0000000 | 0x004a) +#define NT_STATUS_THREAD_IS_TERMINATING (0xC0000000 | 0x004b) +#define NT_STATUS_BAD_WORKING_SET_LIMIT (0xC0000000 | 0x004c) +#define NT_STATUS_INCOMPATIBLE_FILE_MAP (0xC0000000 | 0x004d) +#define NT_STATUS_SECTION_PROTECTION (0xC0000000 | 0x004e) +#define NT_STATUS_EAS_NOT_SUPPORTED (0xC0000000 | 0x004f) +#define NT_STATUS_EA_TOO_LARGE (0xC0000000 | 0x0050) +#define NT_STATUS_NONEXISTENT_EA_ENTRY (0xC0000000 | 0x0051) +#define NT_STATUS_NO_EAS_ON_FILE (0xC0000000 | 0x0052) +#define NT_STATUS_EA_CORRUPT_ERROR (0xC0000000 | 0x0053) +#define NT_STATUS_FILE_LOCK_CONFLICT (0xC0000000 | 0x0054) +#define NT_STATUS_LOCK_NOT_GRANTED (0xC0000000 | 0x0055) +#define NT_STATUS_DELETE_PENDING (0xC0000000 | 0x0056) +#define NT_STATUS_CTL_FILE_NOT_SUPPORTED (0xC0000000 | 0x0057) +#define NT_STATUS_UNKNOWN_REVISION (0xC0000000 | 0x0058) +#define NT_STATUS_REVISION_MISMATCH (0xC0000000 | 0x0059) +#define NT_STATUS_INVALID_OWNER (0xC0000000 | 0x005a) +#define NT_STATUS_INVALID_PRIMARY_GROUP (0xC0000000 | 0x005b) +#define NT_STATUS_NO_IMPERSONATION_TOKEN (0xC0000000 | 0x005c) +#define NT_STATUS_CANT_DISABLE_MANDATORY (0xC0000000 | 0x005d) +#define NT_STATUS_NO_LOGON_SERVERS (0xC0000000 | 0x005e) +#define NT_STATUS_NO_SUCH_LOGON_SESSION (0xC0000000 | 0x005f) +#define NT_STATUS_NO_SUCH_PRIVILEGE (0xC0000000 | 0x0060) +#define NT_STATUS_PRIVILEGE_NOT_HELD (0xC0000000 | 0x0061) +#define NT_STATUS_INVALID_ACCOUNT_NAME (0xC0000000 | 0x0062) +#define NT_STATUS_USER_EXISTS (0xC0000000 | 0x0063) +#define NT_STATUS_NO_SUCH_USER (0xC0000000 | 0x0064) +#define NT_STATUS_GROUP_EXISTS (0xC0000000 | 0x0065) +#define NT_STATUS_NO_SUCH_GROUP (0xC0000000 | 0x0066) +#define NT_STATUS_MEMBER_IN_GROUP (0xC0000000 | 0x0067) +#define NT_STATUS_MEMBER_NOT_IN_GROUP (0xC0000000 | 0x0068) +#define NT_STATUS_LAST_ADMIN (0xC0000000 | 0x0069) +#define NT_STATUS_WRONG_PASSWORD (0xC0000000 | 0x006a) +#define NT_STATUS_ILL_FORMED_PASSWORD (0xC0000000 | 0x006b) +#define NT_STATUS_PASSWORD_RESTRICTION (0xC0000000 | 0x006c) +#define NT_STATUS_LOGON_FAILURE (0xC0000000 | 0x006d) +#define NT_STATUS_ACCOUNT_RESTRICTION (0xC0000000 | 0x006e) +#define NT_STATUS_INVALID_LOGON_HOURS (0xC0000000 | 0x006f) +#define NT_STATUS_INVALID_WORKSTATION (0xC0000000 | 0x0070) +#define NT_STATUS_PASSWORD_EXPIRED (0xC0000000 | 0x0071) +#define NT_STATUS_ACCOUNT_DISABLED (0xC0000000 | 0x0072) +#define NT_STATUS_NONE_MAPPED (0xC0000000 | 0x0073) +#define NT_STATUS_TOO_MANY_LUIDS_REQUESTED (0xC0000000 | 0x0074) +#define NT_STATUS_LUIDS_EXHAUSTED (0xC0000000 | 0x0075) +#define NT_STATUS_INVALID_SUB_AUTHORITY (0xC0000000 | 0x0076) +#define NT_STATUS_INVALID_ACL (0xC0000000 | 0x0077) +#define NT_STATUS_INVALID_SID (0xC0000000 | 0x0078) +#define NT_STATUS_INVALID_SECURITY_DESCR (0xC0000000 | 0x0079) +#define NT_STATUS_PROCEDURE_NOT_FOUND (0xC0000000 | 0x007a) +#define NT_STATUS_INVALID_IMAGE_FORMAT (0xC0000000 | 0x007b) +#define NT_STATUS_NO_TOKEN (0xC0000000 | 0x007c) +#define NT_STATUS_BAD_INHERITANCE_ACL (0xC0000000 | 0x007d) +#define NT_STATUS_RANGE_NOT_LOCKED (0xC0000000 | 0x007e) +#define NT_STATUS_DISK_FULL (0xC0000000 | 0x007f) +#define NT_STATUS_SERVER_DISABLED (0xC0000000 | 0x0080) +#define NT_STATUS_SERVER_NOT_DISABLED (0xC0000000 | 0x0081) +#define NT_STATUS_TOO_MANY_GUIDS_REQUESTED (0xC0000000 | 0x0082) +#define NT_STATUS_GUIDS_EXHAUSTED (0xC0000000 | 0x0083) +#define NT_STATUS_INVALID_ID_AUTHORITY (0xC0000000 | 0x0084) +#define NT_STATUS_AGENTS_EXHAUSTED (0xC0000000 | 0x0085) +#define NT_STATUS_INVALID_VOLUME_LABEL (0xC0000000 | 0x0086) +#define NT_STATUS_SECTION_NOT_EXTENDED (0xC0000000 | 0x0087) +#define NT_STATUS_NOT_MAPPED_DATA (0xC0000000 | 0x0088) +#define NT_STATUS_RESOURCE_DATA_NOT_FOUND (0xC0000000 | 0x0089) +#define NT_STATUS_RESOURCE_TYPE_NOT_FOUND (0xC0000000 | 0x008a) +#define NT_STATUS_RESOURCE_NAME_NOT_FOUND (0xC0000000 | 0x008b) +#define NT_STATUS_ARRAY_BOUNDS_EXCEEDED (0xC0000000 | 0x008c) +#define NT_STATUS_FLOAT_DENORMAL_OPERAND (0xC0000000 | 0x008d) +#define NT_STATUS_FLOAT_DIVIDE_BY_ZERO (0xC0000000 | 0x008e) +#define NT_STATUS_FLOAT_INEXACT_RESULT (0xC0000000 | 0x008f) +#define NT_STATUS_FLOAT_INVALID_OPERATION (0xC0000000 | 0x0090) +#define NT_STATUS_FLOAT_OVERFLOW (0xC0000000 | 0x0091) +#define NT_STATUS_FLOAT_STACK_CHECK (0xC0000000 | 0x0092) +#define NT_STATUS_FLOAT_UNDERFLOW (0xC0000000 | 0x0093) +#define NT_STATUS_INTEGER_DIVIDE_BY_ZERO (0xC0000000 | 0x0094) +#define NT_STATUS_INTEGER_OVERFLOW (0xC0000000 | 0x0095) +#define NT_STATUS_PRIVILEGED_INSTRUCTION (0xC0000000 | 0x0096) +#define NT_STATUS_TOO_MANY_PAGING_FILES (0xC0000000 | 0x0097) +#define NT_STATUS_FILE_INVALID (0xC0000000 | 0x0098) +#define NT_STATUS_ALLOTTED_SPACE_EXCEEDED (0xC0000000 | 0x0099) +#define NT_STATUS_INSUFFICIENT_RESOURCES (0xC0000000 | 0x009a) +#define NT_STATUS_DFS_EXIT_PATH_FOUND (0xC0000000 | 0x009b) +#define NT_STATUS_DEVICE_DATA_ERROR (0xC0000000 | 0x009c) +#define NT_STATUS_DEVICE_NOT_CONNECTED (0xC0000000 | 0x009d) +#define NT_STATUS_DEVICE_POWER_FAILURE (0xC0000000 | 0x009e) +#define NT_STATUS_FREE_VM_NOT_AT_BASE (0xC0000000 | 0x009f) +#define NT_STATUS_MEMORY_NOT_ALLOCATED (0xC0000000 | 0x00a0) +#define NT_STATUS_WORKING_SET_QUOTA (0xC0000000 | 0x00a1) +#define NT_STATUS_MEDIA_WRITE_PROTECTED (0xC0000000 | 0x00a2) +#define NT_STATUS_DEVICE_NOT_READY (0xC0000000 | 0x00a3) +#define NT_STATUS_INVALID_GROUP_ATTRIBUTES (0xC0000000 | 0x00a4) +#define NT_STATUS_BAD_IMPERSONATION_LEVEL (0xC0000000 | 0x00a5) +#define NT_STATUS_CANT_OPEN_ANONYMOUS (0xC0000000 | 0x00a6) +#define NT_STATUS_BAD_VALIDATION_CLASS (0xC0000000 | 0x00a7) +#define NT_STATUS_BAD_TOKEN_TYPE (0xC0000000 | 0x00a8) +#define NT_STATUS_BAD_MASTER_BOOT_RECORD (0xC0000000 | 0x00a9) +#define NT_STATUS_INSTRUCTION_MISALIGNMENT (0xC0000000 | 0x00aa) +#define NT_STATUS_INSTANCE_NOT_AVAILABLE (0xC0000000 | 0x00ab) +#define NT_STATUS_PIPE_NOT_AVAILABLE (0xC0000000 | 0x00ac) +#define NT_STATUS_INVALID_PIPE_STATE (0xC0000000 | 0x00ad) +#define NT_STATUS_PIPE_BUSY (0xC0000000 | 0x00ae) +#define NT_STATUS_ILLEGAL_FUNCTION (0xC0000000 | 0x00af) +#define NT_STATUS_PIPE_DISCONNECTED (0xC0000000 | 0x00b0) +#define NT_STATUS_PIPE_CLOSING (0xC0000000 | 0x00b1) +#define NT_STATUS_PIPE_CONNECTED (0xC0000000 | 0x00b2) +#define NT_STATUS_PIPE_LISTENING (0xC0000000 | 0x00b3) +#define NT_STATUS_INVALID_READ_MODE (0xC0000000 | 0x00b4) +#define NT_STATUS_IO_TIMEOUT (0xC0000000 | 0x00b5) +#define NT_STATUS_FILE_FORCED_CLOSED (0xC0000000 | 0x00b6) +#define NT_STATUS_PROFILING_NOT_STARTED (0xC0000000 | 0x00b7) +#define NT_STATUS_PROFILING_NOT_STOPPED (0xC0000000 | 0x00b8) +#define NT_STATUS_COULD_NOT_INTERPRET (0xC0000000 | 0x00b9) +#define NT_STATUS_FILE_IS_A_DIRECTORY (0xC0000000 | 0x00ba) +#define NT_STATUS_NOT_SUPPORTED (0xC0000000 | 0x00bb) +#define NT_STATUS_REMOTE_NOT_LISTENING (0xC0000000 | 0x00bc) +#define NT_STATUS_DUPLICATE_NAME (0xC0000000 | 0x00bd) +#define NT_STATUS_BAD_NETWORK_PATH (0xC0000000 | 0x00be) +#define NT_STATUS_NETWORK_BUSY (0xC0000000 | 0x00bf) +#define NT_STATUS_DEVICE_DOES_NOT_EXIST (0xC0000000 | 0x00c0) +#define NT_STATUS_TOO_MANY_COMMANDS (0xC0000000 | 0x00c1) +#define NT_STATUS_ADAPTER_HARDWARE_ERROR (0xC0000000 | 0x00c2) +#define NT_STATUS_INVALID_NETWORK_RESPONSE (0xC0000000 | 0x00c3) +#define NT_STATUS_UNEXPECTED_NETWORK_ERROR (0xC0000000 | 0x00c4) +#define NT_STATUS_BAD_REMOTE_ADAPTER (0xC0000000 | 0x00c5) +#define NT_STATUS_PRINT_QUEUE_FULL (0xC0000000 | 0x00c6) +#define NT_STATUS_NO_SPOOL_SPACE (0xC0000000 | 0x00c7) +#define NT_STATUS_PRINT_CANCELLED (0xC0000000 | 0x00c8) +#define NT_STATUS_NETWORK_NAME_DELETED (0xC0000000 | 0x00c9) +#define NT_STATUS_NETWORK_ACCESS_DENIED (0xC0000000 | 0x00ca) +#define NT_STATUS_BAD_DEVICE_TYPE (0xC0000000 | 0x00cb) +#define NT_STATUS_BAD_NETWORK_NAME (0xC0000000 | 0x00cc) +#define NT_STATUS_TOO_MANY_NAMES (0xC0000000 | 0x00cd) +#define NT_STATUS_TOO_MANY_SESSIONS (0xC0000000 | 0x00ce) +#define NT_STATUS_SHARING_PAUSED (0xC0000000 | 0x00cf) +#define NT_STATUS_REQUEST_NOT_ACCEPTED (0xC0000000 | 0x00d0) +#define NT_STATUS_REDIRECTOR_PAUSED (0xC0000000 | 0x00d1) +#define NT_STATUS_NET_WRITE_FAULT (0xC0000000 | 0x00d2) +#define NT_STATUS_PROFILING_AT_LIMIT (0xC0000000 | 0x00d3) +#define NT_STATUS_NOT_SAME_DEVICE (0xC0000000 | 0x00d4) +#define NT_STATUS_FILE_RENAMED (0xC0000000 | 0x00d5) +#define NT_STATUS_VIRTUAL_CIRCUIT_CLOSED (0xC0000000 | 0x00d6) +#define NT_STATUS_NO_SECURITY_ON_OBJECT (0xC0000000 | 0x00d7) +#define NT_STATUS_CANT_WAIT (0xC0000000 | 0x00d8) +#define NT_STATUS_PIPE_EMPTY (0xC0000000 | 0x00d9) +#define NT_STATUS_CANT_ACCESS_DOMAIN_INFO (0xC0000000 | 0x00da) +#define NT_STATUS_CANT_TERMINATE_SELF (0xC0000000 | 0x00db) +#define NT_STATUS_INVALID_SERVER_STATE (0xC0000000 | 0x00dc) +#define NT_STATUS_INVALID_DOMAIN_STATE (0xC0000000 | 0x00dd) +#define NT_STATUS_INVALID_DOMAIN_ROLE (0xC0000000 | 0x00de) +#define NT_STATUS_NO_SUCH_DOMAIN (0xC0000000 | 0x00df) +#define NT_STATUS_DOMAIN_EXISTS (0xC0000000 | 0x00e0) +#define NT_STATUS_DOMAIN_LIMIT_EXCEEDED (0xC0000000 | 0x00e1) +#define NT_STATUS_OPLOCK_NOT_GRANTED (0xC0000000 | 0x00e2) +#define NT_STATUS_INVALID_OPLOCK_PROTOCOL (0xC0000000 | 0x00e3) +#define NT_STATUS_INTERNAL_DB_CORRUPTION (0xC0000000 | 0x00e4) +#define NT_STATUS_INTERNAL_ERROR (0xC0000000 | 0x00e5) +#define NT_STATUS_GENERIC_NOT_MAPPED (0xC0000000 | 0x00e6) +#define NT_STATUS_BAD_DESCRIPTOR_FORMAT (0xC0000000 | 0x00e7) +#define NT_STATUS_INVALID_USER_BUFFER (0xC0000000 | 0x00e8) +#define NT_STATUS_UNEXPECTED_IO_ERROR (0xC0000000 | 0x00e9) +#define NT_STATUS_UNEXPECTED_MM_CREATE_ERR (0xC0000000 | 0x00ea) +#define NT_STATUS_UNEXPECTED_MM_MAP_ERROR (0xC0000000 | 0x00eb) +#define NT_STATUS_UNEXPECTED_MM_EXTEND_ERR (0xC0000000 | 0x00ec) +#define NT_STATUS_NOT_LOGON_PROCESS (0xC0000000 | 0x00ed) +#define NT_STATUS_LOGON_SESSION_EXISTS (0xC0000000 | 0x00ee) +#define NT_STATUS_INVALID_PARAMETER_1 (0xC0000000 | 0x00ef) +#define NT_STATUS_INVALID_PARAMETER_2 (0xC0000000 | 0x00f0) +#define NT_STATUS_INVALID_PARAMETER_3 (0xC0000000 | 0x00f1) +#define NT_STATUS_INVALID_PARAMETER_4 (0xC0000000 | 0x00f2) +#define NT_STATUS_INVALID_PARAMETER_5 (0xC0000000 | 0x00f3) +#define NT_STATUS_INVALID_PARAMETER_6 (0xC0000000 | 0x00f4) +#define NT_STATUS_INVALID_PARAMETER_7 (0xC0000000 | 0x00f5) +#define NT_STATUS_INVALID_PARAMETER_8 (0xC0000000 | 0x00f6) +#define NT_STATUS_INVALID_PARAMETER_9 (0xC0000000 | 0x00f7) +#define NT_STATUS_INVALID_PARAMETER_10 (0xC0000000 | 0x00f8) +#define NT_STATUS_INVALID_PARAMETER_11 (0xC0000000 | 0x00f9) +#define NT_STATUS_INVALID_PARAMETER_12 (0xC0000000 | 0x00fa) +#define NT_STATUS_REDIRECTOR_NOT_STARTED (0xC0000000 | 0x00fb) +#define NT_STATUS_REDIRECTOR_STARTED (0xC0000000 | 0x00fc) +#define NT_STATUS_STACK_OVERFLOW (0xC0000000 | 0x00fd) +#define NT_STATUS_NO_SUCH_PACKAGE (0xC0000000 | 0x00fe) +#define NT_STATUS_BAD_FUNCTION_TABLE (0xC0000000 | 0x00ff) +#define NT_STATUS_DIRECTORY_NOT_EMPTY (0xC0000000 | 0x0101) +#define NT_STATUS_FILE_CORRUPT_ERROR (0xC0000000 | 0x0102) +#define NT_STATUS_NOT_A_DIRECTORY (0xC0000000 | 0x0103) +#define NT_STATUS_BAD_LOGON_SESSION_STATE (0xC0000000 | 0x0104) +#define NT_STATUS_LOGON_SESSION_COLLISION (0xC0000000 | 0x0105) +#define NT_STATUS_NAME_TOO_LONG (0xC0000000 | 0x0106) +#define NT_STATUS_FILES_OPEN (0xC0000000 | 0x0107) +#define NT_STATUS_CONNECTION_IN_USE (0xC0000000 | 0x0108) +#define NT_STATUS_MESSAGE_NOT_FOUND (0xC0000000 | 0x0109) +#define NT_STATUS_PROCESS_IS_TERMINATING (0xC0000000 | 0x010a) +#define NT_STATUS_INVALID_LOGON_TYPE (0xC0000000 | 0x010b) +#define NT_STATUS_NO_GUID_TRANSLATION (0xC0000000 | 0x010c) +#define NT_STATUS_CANNOT_IMPERSONATE (0xC0000000 | 0x010d) +#define NT_STATUS_IMAGE_ALREADY_LOADED (0xC0000000 | 0x010e) +#define NT_STATUS_ABIOS_NOT_PRESENT (0xC0000000 | 0x010f) +#define NT_STATUS_ABIOS_LID_NOT_EXIST (0xC0000000 | 0x0110) +#define NT_STATUS_ABIOS_LID_ALREADY_OWNED (0xC0000000 | 0x0111) +#define NT_STATUS_ABIOS_NOT_LID_OWNER (0xC0000000 | 0x0112) +#define NT_STATUS_ABIOS_INVALID_COMMAND (0xC0000000 | 0x0113) +#define NT_STATUS_ABIOS_INVALID_LID (0xC0000000 | 0x0114) +#define NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE (0xC0000000 | 0x0115) +#define NT_STATUS_ABIOS_INVALID_SELECTOR (0xC0000000 | 0x0116) +#define NT_STATUS_NO_LDT (0xC0000000 | 0x0117) +#define NT_STATUS_INVALID_LDT_SIZE (0xC0000000 | 0x0118) +#define NT_STATUS_INVALID_LDT_OFFSET (0xC0000000 | 0x0119) +#define NT_STATUS_INVALID_LDT_DESCRIPTOR (0xC0000000 | 0x011a) +#define NT_STATUS_INVALID_IMAGE_NE_FORMAT (0xC0000000 | 0x011b) +#define NT_STATUS_RXACT_INVALID_STATE (0xC0000000 | 0x011c) +#define NT_STATUS_RXACT_COMMIT_FAILURE (0xC0000000 | 0x011d) +#define NT_STATUS_MAPPED_FILE_SIZE_ZERO (0xC0000000 | 0x011e) +#define NT_STATUS_TOO_MANY_OPENED_FILES (0xC0000000 | 0x011f) +#define NT_STATUS_CANCELLED (0xC0000000 | 0x0120) +#define NT_STATUS_CANNOT_DELETE (0xC0000000 | 0x0121) +#define NT_STATUS_INVALID_COMPUTER_NAME (0xC0000000 | 0x0122) +#define NT_STATUS_FILE_DELETED (0xC0000000 | 0x0123) +#define NT_STATUS_SPECIAL_ACCOUNT (0xC0000000 | 0x0124) +#define NT_STATUS_SPECIAL_GROUP (0xC0000000 | 0x0125) +#define NT_STATUS_SPECIAL_USER (0xC0000000 | 0x0126) +#define NT_STATUS_MEMBERS_PRIMARY_GROUP (0xC0000000 | 0x0127) +#define NT_STATUS_FILE_CLOSED (0xC0000000 | 0x0128) +#define NT_STATUS_TOO_MANY_THREADS (0xC0000000 | 0x0129) +#define NT_STATUS_THREAD_NOT_IN_PROCESS (0xC0000000 | 0x012a) +#define NT_STATUS_TOKEN_ALREADY_IN_USE (0xC0000000 | 0x012b) +#define NT_STATUS_PAGEFILE_QUOTA_EXCEEDED (0xC0000000 | 0x012c) +#define NT_STATUS_COMMITMENT_LIMIT (0xC0000000 | 0x012d) +#define NT_STATUS_INVALID_IMAGE_LE_FORMAT (0xC0000000 | 0x012e) +#define NT_STATUS_INVALID_IMAGE_NOT_MZ (0xC0000000 | 0x012f) +#define NT_STATUS_INVALID_IMAGE_PROTECT (0xC0000000 | 0x0130) +#define NT_STATUS_INVALID_IMAGE_WIN_16 (0xC0000000 | 0x0131) +#define NT_STATUS_LOGON_SERVER_CONFLICT (0xC0000000 | 0x0132) +#define NT_STATUS_TIME_DIFFERENCE_AT_DC (0xC0000000 | 0x0133) +#define NT_STATUS_SYNCHRONIZATION_REQUIRED (0xC0000000 | 0x0134) +#define NT_STATUS_DLL_NOT_FOUND (0xC0000000 | 0x0135) +#define NT_STATUS_OPEN_FAILED (0xC0000000 | 0x0136) +#define NT_STATUS_IO_PRIVILEGE_FAILED (0xC0000000 | 0x0137) +#define NT_STATUS_ORDINAL_NOT_FOUND (0xC0000000 | 0x0138) +#define NT_STATUS_ENTRYPOINT_NOT_FOUND (0xC0000000 | 0x0139) +#define NT_STATUS_CONTROL_C_EXIT (0xC0000000 | 0x013a) +#define NT_STATUS_LOCAL_DISCONNECT (0xC0000000 | 0x013b) +#define NT_STATUS_REMOTE_DISCONNECT (0xC0000000 | 0x013c) +#define NT_STATUS_REMOTE_RESOURCES (0xC0000000 | 0x013d) +#define NT_STATUS_LINK_FAILED (0xC0000000 | 0x013e) +#define NT_STATUS_LINK_TIMEOUT (0xC0000000 | 0x013f) +#define NT_STATUS_INVALID_CONNECTION (0xC0000000 | 0x0140) +#define NT_STATUS_INVALID_ADDRESS (0xC0000000 | 0x0141) +#define NT_STATUS_DLL_INIT_FAILED (0xC0000000 | 0x0142) +#define NT_STATUS_MISSING_SYSTEMFILE (0xC0000000 | 0x0143) +#define NT_STATUS_UNHANDLED_EXCEPTION (0xC0000000 | 0x0144) +#define NT_STATUS_APP_INIT_FAILURE (0xC0000000 | 0x0145) +#define NT_STATUS_PAGEFILE_CREATE_FAILED (0xC0000000 | 0x0146) +#define NT_STATUS_NO_PAGEFILE (0xC0000000 | 0x0147) +#define NT_STATUS_INVALID_LEVEL (0xC0000000 | 0x0148) +#define NT_STATUS_WRONG_PASSWORD_CORE (0xC0000000 | 0x0149) +#define NT_STATUS_ILLEGAL_FLOAT_CONTEXT (0xC0000000 | 0x014a) +#define NT_STATUS_PIPE_BROKEN (0xC0000000 | 0x014b) +#define NT_STATUS_REGISTRY_CORRUPT (0xC0000000 | 0x014c) +#define NT_STATUS_REGISTRY_IO_FAILED (0xC0000000 | 0x014d) +#define NT_STATUS_NO_EVENT_PAIR (0xC0000000 | 0x014e) +#define NT_STATUS_UNRECOGNIZED_VOLUME (0xC0000000 | 0x014f) +#define NT_STATUS_SERIAL_NO_DEVICE_INITED (0xC0000000 | 0x0150) +#define NT_STATUS_NO_SUCH_ALIAS (0xC0000000 | 0x0151) +#define NT_STATUS_MEMBER_NOT_IN_ALIAS (0xC0000000 | 0x0152) +#define NT_STATUS_MEMBER_IN_ALIAS (0xC0000000 | 0x0153) +#define NT_STATUS_ALIAS_EXISTS (0xC0000000 | 0x0154) +#define NT_STATUS_LOGON_NOT_GRANTED (0xC0000000 | 0x0155) +#define NT_STATUS_TOO_MANY_SECRETS (0xC0000000 | 0x0156) +#define NT_STATUS_SECRET_TOO_LONG (0xC0000000 | 0x0157) +#define NT_STATUS_INTERNAL_DB_ERROR (0xC0000000 | 0x0158) +#define NT_STATUS_FULLSCREEN_MODE (0xC0000000 | 0x0159) +#define NT_STATUS_TOO_MANY_CONTEXT_IDS (0xC0000000 | 0x015a) +#define NT_STATUS_LOGON_TYPE_NOT_GRANTED (0xC0000000 | 0x015b) +#define NT_STATUS_NOT_REGISTRY_FILE (0xC0000000 | 0x015c) +#define NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED (0xC0000000 | 0x015d) +#define NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR (0xC0000000 | 0x015e) +#define NT_STATUS_FT_MISSING_MEMBER (0xC0000000 | 0x015f) +#define NT_STATUS_ILL_FORMED_SERVICE_ENTRY (0xC0000000 | 0x0160) +#define NT_STATUS_ILLEGAL_CHARACTER (0xC0000000 | 0x0161) +#define NT_STATUS_UNMAPPABLE_CHARACTER (0xC0000000 | 0x0162) +#define NT_STATUS_UNDEFINED_CHARACTER (0xC0000000 | 0x0163) +#define NT_STATUS_FLOPPY_VOLUME (0xC0000000 | 0x0164) +#define NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND (0xC0000000 | 0x0165) +#define NT_STATUS_FLOPPY_WRONG_CYLINDER (0xC0000000 | 0x0166) +#define NT_STATUS_FLOPPY_UNKNOWN_ERROR (0xC0000000 | 0x0167) +#define NT_STATUS_FLOPPY_BAD_REGISTERS (0xC0000000 | 0x0168) +#define NT_STATUS_DISK_RECALIBRATE_FAILED (0xC0000000 | 0x0169) +#define NT_STATUS_DISK_OPERATION_FAILED (0xC0000000 | 0x016a) +#define NT_STATUS_DISK_RESET_FAILED (0xC0000000 | 0x016b) +#define NT_STATUS_SHARED_IRQ_BUSY (0xC0000000 | 0x016c) +#define NT_STATUS_FT_ORPHANING (0xC0000000 | 0x016d) +#define NT_STATUS_PARTITION_FAILURE (0xC0000000 | 0x0172) +#define NT_STATUS_INVALID_BLOCK_LENGTH (0xC0000000 | 0x0173) +#define NT_STATUS_DEVICE_NOT_PARTITIONED (0xC0000000 | 0x0174) +#define NT_STATUS_UNABLE_TO_LOCK_MEDIA (0xC0000000 | 0x0175) +#define NT_STATUS_UNABLE_TO_UNLOAD_MEDIA (0xC0000000 | 0x0176) +#define NT_STATUS_EOM_OVERFLOW (0xC0000000 | 0x0177) +#define NT_STATUS_NO_MEDIA (0xC0000000 | 0x0178) +#define NT_STATUS_NO_SUCH_MEMBER (0xC0000000 | 0x017a) +#define NT_STATUS_INVALID_MEMBER (0xC0000000 | 0x017b) +#define NT_STATUS_KEY_DELETED (0xC0000000 | 0x017c) +#define NT_STATUS_NO_LOG_SPACE (0xC0000000 | 0x017d) +#define NT_STATUS_TOO_MANY_SIDS (0xC0000000 | 0x017e) +#define NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED (0xC0000000 | 0x017f) +#define NT_STATUS_KEY_HAS_CHILDREN (0xC0000000 | 0x0180) +#define NT_STATUS_CHILD_MUST_BE_VOLATILE (0xC0000000 | 0x0181) +#define NT_STATUS_DEVICE_CONFIGURATION_ERROR (0xC0000000 | 0x0182) +#define NT_STATUS_DRIVER_INTERNAL_ERROR (0xC0000000 | 0x0183) +#define NT_STATUS_INVALID_DEVICE_STATE (0xC0000000 | 0x0184) +#define NT_STATUS_IO_DEVICE_ERROR (0xC0000000 | 0x0185) +#define NT_STATUS_DEVICE_PROTOCOL_ERROR (0xC0000000 | 0x0186) +#define NT_STATUS_BACKUP_CONTROLLER (0xC0000000 | 0x0187) +#define NT_STATUS_LOG_FILE_FULL (0xC0000000 | 0x0188) +#define NT_STATUS_TOO_LATE (0xC0000000 | 0x0189) +#define NT_STATUS_NO_TRUST_LSA_SECRET (0xC0000000 | 0x018a) +#define NT_STATUS_NO_TRUST_SAM_ACCOUNT (0xC0000000 | 0x018b) +#define NT_STATUS_TRUSTED_DOMAIN_FAILURE (0xC0000000 | 0x018c) +#define NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE (0xC0000000 | 0x018d) +#define NT_STATUS_EVENTLOG_FILE_CORRUPT (0xC0000000 | 0x018e) +#define NT_STATUS_EVENTLOG_CANT_START (0xC0000000 | 0x018f) +#define NT_STATUS_TRUST_FAILURE (0xC0000000 | 0x0190) +#define NT_STATUS_MUTANT_LIMIT_EXCEEDED (0xC0000000 | 0x0191) +#define NT_STATUS_NETLOGON_NOT_STARTED (0xC0000000 | 0x0192) +#define NT_STATUS_ACCOUNT_EXPIRED (0xC0000000 | 0x0193) +#define NT_STATUS_POSSIBLE_DEADLOCK (0xC0000000 | 0x0194) +#define NT_STATUS_NETWORK_CREDENTIAL_CONFLICT (0xC0000000 | 0x0195) +#define NT_STATUS_REMOTE_SESSION_LIMIT (0xC0000000 | 0x0196) +#define NT_STATUS_EVENTLOG_FILE_CHANGED (0xC0000000 | 0x0197) +#define NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT (0xC0000000 | 0x0198) +#define NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT (0xC0000000 | 0x0199) +#define NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT (0xC0000000 | 0x019a) +#define NT_STATUS_DOMAIN_TRUST_INCONSISTENT (0xC0000000 | 0x019b) +#define NT_STATUS_FS_DRIVER_REQUIRED (0xC0000000 | 0x019c) +#define NT_STATUS_NO_USER_SESSION_KEY (0xC0000000 | 0x0202) +#define NT_STATUS_USER_SESSION_DELETED (0xC0000000 | 0x0203) +#define NT_STATUS_RESOURCE_LANG_NOT_FOUND (0xC0000000 | 0x0204) +#define NT_STATUS_INSUFF_SERVER_RESOURCES (0xC0000000 | 0x0205) +#define NT_STATUS_INVALID_BUFFER_SIZE (0xC0000000 | 0x0206) +#define NT_STATUS_INVALID_ADDRESS_COMPONENT (0xC0000000 | 0x0207) +#define NT_STATUS_INVALID_ADDRESS_WILDCARD (0xC0000000 | 0x0208) +#define NT_STATUS_TOO_MANY_ADDRESSES (0xC0000000 | 0x0209) +#define NT_STATUS_ADDRESS_ALREADY_EXISTS (0xC0000000 | 0x020a) +#define NT_STATUS_ADDRESS_CLOSED (0xC0000000 | 0x020b) +#define NT_STATUS_CONNECTION_DISCONNECTED (0xC0000000 | 0x020c) +#define NT_STATUS_CONNECTION_RESET (0xC0000000 | 0x020d) +#define NT_STATUS_TOO_MANY_NODES (0xC0000000 | 0x020e) +#define NT_STATUS_TRANSACTION_ABORTED (0xC0000000 | 0x020f) +#define NT_STATUS_TRANSACTION_TIMED_OUT (0xC0000000 | 0x0210) +#define NT_STATUS_TRANSACTION_NO_RELEASE (0xC0000000 | 0x0211) +#define NT_STATUS_TRANSACTION_NO_MATCH (0xC0000000 | 0x0212) +#define NT_STATUS_TRANSACTION_RESPONDED (0xC0000000 | 0x0213) +#define NT_STATUS_TRANSACTION_INVALID_ID (0xC0000000 | 0x0214) +#define NT_STATUS_TRANSACTION_INVALID_TYPE (0xC0000000 | 0x0215) +#define NT_STATUS_NOT_SERVER_SESSION (0xC0000000 | 0x0216) +#define NT_STATUS_NOT_CLIENT_SESSION (0xC0000000 | 0x0217) +#define NT_STATUS_CANNOT_LOAD_REGISTRY_FILE (0xC0000000 | 0x0218) +#define NT_STATUS_DEBUG_ATTACH_FAILED (0xC0000000 | 0x0219) +#define NT_STATUS_SYSTEM_PROCESS_TERMINATED (0xC0000000 | 0x021a) +#define NT_STATUS_DATA_NOT_ACCEPTED (0xC0000000 | 0x021b) +#define NT_STATUS_NO_BROWSER_SERVERS_FOUND (0xC0000000 | 0x021c) +#define NT_STATUS_VDM_HARD_ERROR (0xC0000000 | 0x021d) +#define NT_STATUS_DRIVER_CANCEL_TIMEOUT (0xC0000000 | 0x021e) +#define NT_STATUS_REPLY_MESSAGE_MISMATCH (0xC0000000 | 0x021f) +#define NT_STATUS_MAPPED_ALIGNMENT (0xC0000000 | 0x0220) +#define NT_STATUS_IMAGE_CHECKSUM_MISMATCH (0xC0000000 | 0x0221) +#define NT_STATUS_LOST_WRITEBEHIND_DATA (0xC0000000 | 0x0222) +#define NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID (0xC0000000 | 0x0223) +#define NT_STATUS_PASSWORD_MUST_CHANGE (0xC0000000 | 0x0224) +#define NT_STATUS_NOT_FOUND (0xC0000000 | 0x0225) +#define NT_STATUS_NOT_TINY_STREAM (0xC0000000 | 0x0226) +#define NT_STATUS_RECOVERY_FAILURE (0xC0000000 | 0x0227) +#define NT_STATUS_STACK_OVERFLOW_READ (0xC0000000 | 0x0228) +#define NT_STATUS_FAIL_CHECK (0xC0000000 | 0x0229) +#define NT_STATUS_DUPLICATE_OBJECTID (0xC0000000 | 0x022a) +#define NT_STATUS_OBJECTID_EXISTS (0xC0000000 | 0x022b) +#define NT_STATUS_CONVERT_TO_LARGE (0xC0000000 | 0x022c) +#define NT_STATUS_RETRY (0xC0000000 | 0x022d) +#define NT_STATUS_FOUND_OUT_OF_SCOPE (0xC0000000 | 0x022e) +#define NT_STATUS_ALLOCATE_BUCKET (0xC0000000 | 0x022f) +#define NT_STATUS_PROPSET_NOT_FOUND (0xC0000000 | 0x0230) +#define NT_STATUS_MARSHALL_OVERFLOW (0xC0000000 | 0x0231) +#define NT_STATUS_INVALID_VARIANT (0xC0000000 | 0x0232) +#define NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND (0xC0000000 | 0x0233) +#define NT_STATUS_ACCOUNT_LOCKED_OUT (0xC0000000 | 0x0234) +#define NT_STATUS_HANDLE_NOT_CLOSABLE (0xC0000000 | 0x0235) +#define NT_STATUS_CONNECTION_REFUSED (0xC0000000 | 0x0236) +#define NT_STATUS_GRACEFUL_DISCONNECT (0xC0000000 | 0x0237) +#define NT_STATUS_ADDRESS_ALREADY_ASSOCIATED (0xC0000000 | 0x0238) +#define NT_STATUS_ADDRESS_NOT_ASSOCIATED (0xC0000000 | 0x0239) +#define NT_STATUS_CONNECTION_INVALID (0xC0000000 | 0x023a) +#define NT_STATUS_CONNECTION_ACTIVE (0xC0000000 | 0x023b) +#define NT_STATUS_NETWORK_UNREACHABLE (0xC0000000 | 0x023c) +#define NT_STATUS_HOST_UNREACHABLE (0xC0000000 | 0x023d) +#define NT_STATUS_PROTOCOL_UNREACHABLE (0xC0000000 | 0x023e) +#define NT_STATUS_PORT_UNREACHABLE (0xC0000000 | 0x023f) +#define NT_STATUS_REQUEST_ABORTED (0xC0000000 | 0x0240) +#define NT_STATUS_CONNECTION_ABORTED (0xC0000000 | 0x0241) +#define NT_STATUS_BAD_COMPRESSION_BUFFER (0xC0000000 | 0x0242) +#define NT_STATUS_USER_MAPPED_FILE (0xC0000000 | 0x0243) +#define NT_STATUS_AUDIT_FAILED (0xC0000000 | 0x0244) +#define NT_STATUS_TIMER_RESOLUTION_NOT_SET (0xC0000000 | 0x0245) +#define NT_STATUS_CONNECTION_COUNT_LIMIT (0xC0000000 | 0x0246) +#define NT_STATUS_LOGIN_TIME_RESTRICTION (0xC0000000 | 0x0247) +#define NT_STATUS_LOGIN_WKSTA_RESTRICTION (0xC0000000 | 0x0248) +#define NT_STATUS_IMAGE_MP_UP_MISMATCH (0xC0000000 | 0x0249) +#define NT_STATUS_INSUFFICIENT_LOGON_INFO (0xC0000000 | 0x0250) +#define NT_STATUS_BAD_DLL_ENTRYPOINT (0xC0000000 | 0x0251) +#define NT_STATUS_BAD_SERVICE_ENTRYPOINT (0xC0000000 | 0x0252) +#define NT_STATUS_LPC_REPLY_LOST (0xC0000000 | 0x0253) +#define NT_STATUS_IP_ADDRESS_CONFLICT1 (0xC0000000 | 0x0254) +#define NT_STATUS_IP_ADDRESS_CONFLICT2 (0xC0000000 | 0x0255) +#define NT_STATUS_REGISTRY_QUOTA_LIMIT (0xC0000000 | 0x0256) +#define NT_STATUS_PATH_NOT_COVERED (0xC0000000 | 0x0257) +#define NT_STATUS_NO_CALLBACK_ACTIVE (0xC0000000 | 0x0258) +#define NT_STATUS_LICENSE_QUOTA_EXCEEDED (0xC0000000 | 0x0259) +#define NT_STATUS_PWD_TOO_SHORT (0xC0000000 | 0x025a) +#define NT_STATUS_PWD_TOO_RECENT (0xC0000000 | 0x025b) +#define NT_STATUS_PWD_HISTORY_CONFLICT (0xC0000000 | 0x025c) +#define NT_STATUS_PLUGPLAY_NO_DEVICE (0xC0000000 | 0x025e) +#define NT_STATUS_UNSUPPORTED_COMPRESSION (0xC0000000 | 0x025f) +#define NT_STATUS_INVALID_HW_PROFILE (0xC0000000 | 0x0260) +#define NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH (0xC0000000 | 0x0261) +#define NT_STATUS_DRIVER_ORDINAL_NOT_FOUND (0xC0000000 | 0x0262) +#define NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND (0xC0000000 | 0x0263) +#define NT_STATUS_RESOURCE_NOT_OWNED (0xC0000000 | 0x0264) +#define NT_STATUS_TOO_MANY_LINKS (0xC0000000 | 0x0265) +#define NT_STATUS_QUOTA_LIST_INCONSISTENT (0xC0000000 | 0x0266) +#define NT_STATUS_FILE_IS_OFFLINE (0xC0000000 | 0x0267) +#define NT_STATUS_NETWORK_SESSION_EXPIRED (0xC0000000 | 0x035c) +#define NT_STATUS_NO_SUCH_JOB (0xC0000000 | 0xEDE) /* scheduler */ +#define NT_STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP (0xC0000000 | 0x5D0000) +#define NT_STATUS_PENDING 0x00000103 +#endif /* _NTERR_H */ diff --git a/fs/ksmbd/ntlmssp.h b/fs/ksmbd/ntlmssp.h new file mode 100644 index 0000000000000..adaf4c0cbe8fd --- /dev/null +++ b/fs/ksmbd/ntlmssp.h @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/* + * Copyright (c) International Business Machines Corp., 2002,2007 + * Author(s): Steve French (sfrench@us.ibm.com) + */ + +#ifndef __KSMBD_NTLMSSP_H +#define __KSMBD_NTLMSSP_H + +#define NTLMSSP_SIGNATURE "NTLMSSP" + +/* Security blob target info data */ +#define TGT_Name "KSMBD" + +/* + * Size of the crypto key returned on the negotiate SMB in bytes + */ +#define CIFS_CRYPTO_KEY_SIZE (8) +#define CIFS_KEY_SIZE (40) + +/* + * Size of encrypted user password in bytes + */ +#define CIFS_ENCPWD_SIZE (16) +#define CIFS_CPHTXT_SIZE (16) + +/* Message Types */ +#define NtLmNegotiate cpu_to_le32(1) +#define NtLmChallenge cpu_to_le32(2) +#define NtLmAuthenticate cpu_to_le32(3) +#define UnknownMessage cpu_to_le32(8) + +/* Negotiate Flags */ +#define NTLMSSP_NEGOTIATE_UNICODE 0x01 /* Text strings are unicode */ +#define NTLMSSP_NEGOTIATE_OEM 0x02 /* Text strings are in OEM */ +#define NTLMSSP_REQUEST_TARGET 0x04 /* Srv returns its auth realm */ +/* define reserved9 0x08 */ +#define NTLMSSP_NEGOTIATE_SIGN 0x0010 /* Request signing capability */ +#define NTLMSSP_NEGOTIATE_SEAL 0x0020 /* Request confidentiality */ +#define NTLMSSP_NEGOTIATE_DGRAM 0x0040 +#define NTLMSSP_NEGOTIATE_LM_KEY 0x0080 /* Use LM session key */ +/* defined reserved 8 0x0100 */ +#define NTLMSSP_NEGOTIATE_NTLM 0x0200 /* NTLM authentication */ +#define NTLMSSP_NEGOTIATE_NT_ONLY 0x0400 /* Lanman not allowed */ +#define NTLMSSP_ANONYMOUS 0x0800 +#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x1000 /* reserved6 */ +#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x2000 +#define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x4000 /* client/server same machine */ +#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x8000 /* Sign. All security levels */ +#define NTLMSSP_TARGET_TYPE_DOMAIN 0x10000 +#define NTLMSSP_TARGET_TYPE_SERVER 0x20000 +#define NTLMSSP_TARGET_TYPE_SHARE 0x40000 +#define NTLMSSP_NEGOTIATE_EXTENDED_SEC 0x80000 /* NB:not related to NTLMv2 pwd*/ +/* #define NTLMSSP_REQUEST_INIT_RESP 0x100000 */ +#define NTLMSSP_NEGOTIATE_IDENTIFY 0x100000 +#define NTLMSSP_REQUEST_ACCEPT_RESP 0x200000 /* reserved5 */ +#define NTLMSSP_REQUEST_NON_NT_KEY 0x400000 +#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x800000 +/* #define reserved4 0x1000000 */ +#define NTLMSSP_NEGOTIATE_VERSION 0x2000000 /* we do not set */ +/* #define reserved3 0x4000000 */ +/* #define reserved2 0x8000000 */ +/* #define reserved1 0x10000000 */ +#define NTLMSSP_NEGOTIATE_128 0x20000000 +#define NTLMSSP_NEGOTIATE_KEY_XCH 0x40000000 +#define NTLMSSP_NEGOTIATE_56 0x80000000 + +/* Define AV Pair Field IDs */ +enum av_field_type { + NTLMSSP_AV_EOL = 0, + NTLMSSP_AV_NB_COMPUTER_NAME, + NTLMSSP_AV_NB_DOMAIN_NAME, + NTLMSSP_AV_DNS_COMPUTER_NAME, + NTLMSSP_AV_DNS_DOMAIN_NAME, + NTLMSSP_AV_DNS_TREE_NAME, + NTLMSSP_AV_FLAGS, + NTLMSSP_AV_TIMESTAMP, + NTLMSSP_AV_RESTRICTION, + NTLMSSP_AV_TARGET_NAME, + NTLMSSP_AV_CHANNEL_BINDINGS +}; + +/* Although typedefs are not commonly used for structure definitions */ +/* in the Linux kernel, in this particular case they are useful */ +/* to more closely match the standards document for NTLMSSP from */ +/* OpenGroup and to make the code more closely match the standard in */ +/* appearance */ + +struct security_buffer { + __le16 Length; + __le16 MaximumLength; + __le32 BufferOffset; /* offset to buffer */ +} __packed; + +struct target_info { + __le16 Type; + __le16 Length; + __u8 Content[0]; +} __packed; + +struct negotiate_message { + __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; + __le32 MessageType; /* NtLmNegotiate = 1 */ + __le32 NegotiateFlags; + struct security_buffer DomainName; /* RFC 1001 style and ASCII */ + struct security_buffer WorkstationName; /* RFC 1001 and ASCII */ + /* + * struct security_buffer for version info not present since we + * do not set the version is present flag + */ + char DomainString[0]; + /* followed by WorkstationString */ +} __packed; + +struct challenge_message { + __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; + __le32 MessageType; /* NtLmChallenge = 2 */ + struct security_buffer TargetName; + __le32 NegotiateFlags; + __u8 Challenge[CIFS_CRYPTO_KEY_SIZE]; + __u8 Reserved[8]; + struct security_buffer TargetInfoArray; + /* + * struct security_buffer for version info not present since we + * do not set the version is present flag + */ +} __packed; + +struct authenticate_message { + __u8 Signature[sizeof(NTLMSSP_SIGNATURE)]; + __le32 MessageType; /* NtLmsAuthenticate = 3 */ + struct security_buffer LmChallengeResponse; + struct security_buffer NtChallengeResponse; + struct security_buffer DomainName; + struct security_buffer UserName; + struct security_buffer WorkstationName; + struct security_buffer SessionKey; + __le32 NegotiateFlags; + /* + * struct security_buffer for version info not present since we + * do not set the version is present flag + */ + char UserString[0]; +} __packed; + +struct ntlmv2_resp { + char ntlmv2_hash[CIFS_ENCPWD_SIZE]; + __le32 blob_signature; + __u32 reserved; + __le64 time; + __u64 client_chal; /* random */ + __u32 reserved2; + /* array of name entries could follow ending in minimum 4 byte struct */ +} __packed; + +/* per smb session structure/fields */ +struct ntlmssp_auth { + /* whether session key is per smb session */ + bool sesskey_per_smbsess; + /* sent by client in type 1 ntlmsssp exchange */ + __u32 client_flags; + /* sent by server in type 2 ntlmssp exchange */ + __u32 conn_flags; + /* sent to server */ + unsigned char ciphertext[CIFS_CPHTXT_SIZE]; + /* used by ntlmssp */ + char cryptkey[CIFS_CRYPTO_KEY_SIZE]; +}; +#endif /* __KSMBD_NTLMSSP_H */ diff --git a/fs/ksmbd/oplock.c b/fs/ksmbd/oplock.c new file mode 100644 index 0000000000000..c0361670574cc --- /dev/null +++ b/fs/ksmbd/oplock.c @@ -0,0 +1,1994 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include + +#include "glob.h" +#include "oplock.h" + +#include "smb_common.h" +#ifdef CONFIG_SMB_INSECURE_SERVER +#include "smb1pdu.h" +#endif +#include "smbstatus.h" +#include "connection.h" +#include "mgmt/user_session.h" +#include "mgmt/share_config.h" +#include "mgmt/tree_connect.h" + +static LIST_HEAD(lease_table_list); +static DEFINE_RWLOCK(lease_list_lock); + +/** + * alloc_opinfo() - allocate a new opinfo object for oplock info + * @work: smb work + * @id: fid of open file + * @Tid: tree id of connection + * + * Return: allocated opinfo object on success, otherwise NULL + */ +static struct oplock_info *alloc_opinfo(struct ksmbd_work *work, + u64 id, __u16 Tid) +{ + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + struct oplock_info *opinfo; + + opinfo = kzalloc(sizeof(struct oplock_info), GFP_KERNEL); + if (!opinfo) + return NULL; + + opinfo->sess = sess; + opinfo->conn = conn; + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + opinfo->op_state = OPLOCK_STATE_NONE; + opinfo->pending_break = 0; + opinfo->fid = id; + opinfo->Tid = Tid; +#ifdef CONFIG_SMB_INSECURE_SERVER + opinfo->is_smb2 = IS_SMB2(conn); +#endif + INIT_LIST_HEAD(&opinfo->op_entry); + INIT_LIST_HEAD(&opinfo->interim_list); + init_waitqueue_head(&opinfo->oplock_q); + init_waitqueue_head(&opinfo->oplock_brk); + atomic_set(&opinfo->refcount, 1); + atomic_set(&opinfo->breaking_cnt, 0); + + return opinfo; +} + +static void lease_add_list(struct oplock_info *opinfo) +{ + struct lease_table *lb = opinfo->o_lease->l_lb; + + spin_lock(&lb->lb_lock); + list_add_rcu(&opinfo->lease_entry, &lb->lease_list); + spin_unlock(&lb->lb_lock); +} + +static void lease_del_list(struct oplock_info *opinfo) +{ + struct lease_table *lb = opinfo->o_lease->l_lb; + + if (!lb) + return; + + spin_lock(&lb->lb_lock); + if (list_empty(&opinfo->lease_entry)) { + spin_unlock(&lb->lb_lock); + return; + } + + list_del_init(&opinfo->lease_entry); + opinfo->o_lease->l_lb = NULL; + spin_unlock(&lb->lb_lock); +} + +static void lb_add(struct lease_table *lb) +{ + write_lock(&lease_list_lock); + list_add(&lb->l_entry, &lease_table_list); + write_unlock(&lease_list_lock); +} + +static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx) +{ + struct lease *lease; + + lease = kmalloc(sizeof(struct lease), GFP_KERNEL); + if (!lease) + return -ENOMEM; + + memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); + lease->state = lctx->req_state; + lease->new_state = 0; + lease->flags = lctx->flags; + lease->duration = lctx->duration; + memcpy(lease->parent_lease_key, lctx->parent_lease_key, SMB2_LEASE_KEY_SIZE); + lease->version = lctx->version; + lease->epoch = 0; + INIT_LIST_HEAD(&opinfo->lease_entry); + opinfo->o_lease = lease; + + return 0; +} + +static void free_lease(struct oplock_info *opinfo) +{ + struct lease *lease; + + lease = opinfo->o_lease; + kfree(lease); +} + +static void free_opinfo(struct oplock_info *opinfo) +{ + if (opinfo->is_lease) + free_lease(opinfo); + kfree(opinfo); +} + +static inline void opinfo_free_rcu(struct rcu_head *rcu_head) +{ + struct oplock_info *opinfo; + + opinfo = container_of(rcu_head, struct oplock_info, rcu_head); + free_opinfo(opinfo); +} + +struct oplock_info *opinfo_get(struct ksmbd_file *fp) +{ + struct oplock_info *opinfo; + + rcu_read_lock(); + opinfo = rcu_dereference(fp->f_opinfo); + if (opinfo && !atomic_inc_not_zero(&opinfo->refcount)) + opinfo = NULL; + rcu_read_unlock(); + + return opinfo; +} + +static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci) +{ + struct oplock_info *opinfo; + + if (list_empty(&ci->m_op_list)) + return NULL; + + rcu_read_lock(); + opinfo = list_first_or_null_rcu(&ci->m_op_list, struct oplock_info, + op_entry); + if (opinfo && !atomic_inc_not_zero(&opinfo->refcount)) + opinfo = NULL; + rcu_read_unlock(); + + return opinfo; +} + +void opinfo_put(struct oplock_info *opinfo) +{ + if (!atomic_dec_and_test(&opinfo->refcount)) + return; + + call_rcu(&opinfo->rcu_head, opinfo_free_rcu); +} + +static void opinfo_add(struct oplock_info *opinfo) +{ + struct ksmbd_inode *ci = opinfo->o_fp->f_ci; + + write_lock(&ci->m_lock); + list_add_rcu(&opinfo->op_entry, &ci->m_op_list); + write_unlock(&ci->m_lock); +} + +static void opinfo_del(struct oplock_info *opinfo) +{ + struct ksmbd_inode *ci = opinfo->o_fp->f_ci; + + if (opinfo->is_lease) { + write_lock(&lease_list_lock); + lease_del_list(opinfo); + write_unlock(&lease_list_lock); + } + write_lock(&ci->m_lock); + list_del_rcu(&opinfo->op_entry); + write_unlock(&ci->m_lock); +} + +static unsigned long opinfo_count(struct ksmbd_file *fp) +{ + if (ksmbd_stream_fd(fp)) + return atomic_read(&fp->f_ci->sop_count); + else + return atomic_read(&fp->f_ci->op_count); +} + +static void opinfo_count_inc(struct ksmbd_file *fp) +{ + if (ksmbd_stream_fd(fp)) + return atomic_inc(&fp->f_ci->sop_count); + else + return atomic_inc(&fp->f_ci->op_count); +} + +static void opinfo_count_dec(struct ksmbd_file *fp) +{ + if (ksmbd_stream_fd(fp)) + return atomic_dec(&fp->f_ci->sop_count); + else + return atomic_dec(&fp->f_ci->op_count); +} + +/** + * opinfo_write_to_read() - convert a write oplock to read oplock + * @opinfo: current oplock info + * + * Return: 0 on success, otherwise -EINVAL + */ +int opinfo_write_to_read(struct oplock_info *opinfo) +{ + struct lease *lease = opinfo->o_lease; + +#ifdef CONFIG_SMB_INSECURE_SERVER + if (opinfo->is_smb2) { + if (!(opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || + opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) { + pr_err("bad oplock(0x%x)\n", opinfo->level); + if (opinfo->is_lease) + pr_err("lease state(0x%x)\n", lease->state); + return -EINVAL; + } + opinfo->level = SMB2_OPLOCK_LEVEL_II; + + if (opinfo->is_lease) + lease->state = lease->new_state; + } else { + if (!(opinfo->level == OPLOCK_EXCLUSIVE || + opinfo->level == OPLOCK_BATCH)) { + pr_err("bad oplock(0x%x)\n", opinfo->level); + return -EINVAL; + } + opinfo->level = OPLOCK_READ; + } +#else + if (!(opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || + opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) { + pr_err("bad oplock(0x%x)\n", opinfo->level); + if (opinfo->is_lease) + pr_err("lease state(0x%x)\n", lease->state); + return -EINVAL; + } + opinfo->level = SMB2_OPLOCK_LEVEL_II; + + if (opinfo->is_lease) + lease->state = lease->new_state; +#endif + return 0; +} + +/** + * opinfo_read_handle_to_read() - convert a read/handle oplock to read oplock + * @opinfo: current oplock info + * + * Return: 0 on success, otherwise -EINVAL + */ +int opinfo_read_handle_to_read(struct oplock_info *opinfo) +{ + struct lease *lease = opinfo->o_lease; + + lease->state = lease->new_state; + opinfo->level = SMB2_OPLOCK_LEVEL_II; + return 0; +} + +/** + * opinfo_write_to_none() - convert a write oplock to none + * @opinfo: current oplock info + * + * Return: 0 on success, otherwise -EINVAL + */ +int opinfo_write_to_none(struct oplock_info *opinfo) +{ + struct lease *lease = opinfo->o_lease; + +#ifdef CONFIG_SMB_INSECURE_SERVER + if (opinfo->is_smb2) { + if (!(opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || + opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) { + pr_err("bad oplock(0x%x)\n", opinfo->level); + if (opinfo->is_lease) + pr_err("lease state(0x%x)\n", lease->state); + return -EINVAL; + } + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + if (opinfo->is_lease) + lease->state = lease->new_state; + } else { + if (!(opinfo->level == OPLOCK_EXCLUSIVE || + opinfo->level == OPLOCK_BATCH)) { + pr_err("bad oplock(0x%x)\n", opinfo->level); + return -EINVAL; + } + opinfo->level = OPLOCK_NONE; + } +#else + if (!(opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || + opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) { + pr_err("bad oplock(0x%x)\n", opinfo->level); + if (opinfo->is_lease) + pr_err("lease state(0x%x)\n", lease->state); + return -EINVAL; + } + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + if (opinfo->is_lease) + lease->state = lease->new_state; +#endif + return 0; +} + +/** + * opinfo_read_to_none() - convert a write read to none + * @opinfo: current oplock info + * + * Return: 0 on success, otherwise -EINVAL + */ +int opinfo_read_to_none(struct oplock_info *opinfo) +{ + struct lease *lease = opinfo->o_lease; + +#ifdef CONFIG_SMB_INSECURE_SERVER + if (opinfo->is_smb2) { + if (opinfo->level != SMB2_OPLOCK_LEVEL_II) { + pr_err("bad oplock(0x%x)\n", opinfo->level); + if (opinfo->is_lease) + pr_err("lease state(0x%x)\n", lease->state); + return -EINVAL; + } + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + if (opinfo->is_lease) + lease->state = lease->new_state; + } else { + if (opinfo->level != OPLOCK_READ) { + pr_err("bad oplock(0x%x)\n", opinfo->level); + return -EINVAL; + } + opinfo->level = OPLOCK_NONE; + } +#else + if (opinfo->level != SMB2_OPLOCK_LEVEL_II) { + pr_err("bad oplock(0x%x)\n", opinfo->level); + if (opinfo->is_lease) + pr_err("lease state(0x%x)\n", lease->state); + return -EINVAL; + } + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + if (opinfo->is_lease) + lease->state = lease->new_state; +#endif + return 0; +} + +/** + * lease_read_to_write() - upgrade lease state from read to write + * @opinfo: current lease info + * + * Return: 0 on success, otherwise -EINVAL + */ +int lease_read_to_write(struct oplock_info *opinfo) +{ + struct lease *lease = opinfo->o_lease; + + if (!(lease->state & SMB2_LEASE_READ_CACHING_LE)) { + ksmbd_debug(OPLOCK, "bad lease state(0x%x)\n", lease->state); + return -EINVAL; + } + + lease->new_state = SMB2_LEASE_NONE_LE; + lease->state |= SMB2_LEASE_WRITE_CACHING_LE; + if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) + opinfo->level = SMB2_OPLOCK_LEVEL_BATCH; + else + opinfo->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + return 0; +} + +/** + * lease_none_upgrade() - upgrade lease state from none + * @opinfo: current lease info + * @new_state: new lease state + * + * Return: 0 on success, otherwise -EINVAL + */ +static int lease_none_upgrade(struct oplock_info *opinfo, __le32 new_state) +{ + struct lease *lease = opinfo->o_lease; + + if (!(lease->state == SMB2_LEASE_NONE_LE)) { + ksmbd_debug(OPLOCK, "bad lease state(0x%x)\n", lease->state); + return -EINVAL; + } + + lease->new_state = SMB2_LEASE_NONE_LE; + lease->state = new_state; + if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) + if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) + opinfo->level = SMB2_OPLOCK_LEVEL_BATCH; + else + opinfo->level = SMB2_OPLOCK_LEVEL_II; + else if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) + opinfo->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + else if (lease->state & SMB2_LEASE_READ_CACHING_LE) + opinfo->level = SMB2_OPLOCK_LEVEL_II; + + return 0; +} + +/** + * close_id_del_oplock() - release oplock object at file close time + * @fp: ksmbd file pointer + */ +void close_id_del_oplock(struct ksmbd_file *fp) +{ + struct oplock_info *opinfo; + + if (S_ISDIR(file_inode(fp->filp)->i_mode)) + return; + + opinfo = opinfo_get(fp); + if (!opinfo) + return; + + opinfo_del(opinfo); + + rcu_assign_pointer(fp->f_opinfo, NULL); + if (opinfo->op_state == OPLOCK_ACK_WAIT) { + opinfo->op_state = OPLOCK_CLOSING; + wake_up_interruptible_all(&opinfo->oplock_q); + if (opinfo->is_lease) { + atomic_set(&opinfo->breaking_cnt, 0); + wake_up_interruptible_all(&opinfo->oplock_brk); + } + } + + opinfo_count_dec(fp); + atomic_dec(&opinfo->refcount); + opinfo_put(opinfo); +} + +/** + * grant_write_oplock() - grant exclusive/batch oplock or write lease + * @opinfo_new: new oplock info object + * @req_oplock: request oplock + * @lctx: lease context information + * + * Return: 0 + */ +static void grant_write_oplock(struct oplock_info *opinfo_new, int req_oplock, + struct lease_ctx_info *lctx) +{ + struct lease *lease = opinfo_new->o_lease; + +#ifdef CONFIG_SMB_INSECURE_SERVER + if (opinfo_new->is_smb2) { + if (req_oplock == SMB2_OPLOCK_LEVEL_BATCH) + opinfo_new->level = SMB2_OPLOCK_LEVEL_BATCH; + else + opinfo_new->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + } else { + if (req_oplock == REQ_BATCHOPLOCK) + opinfo_new->level = OPLOCK_BATCH; + else + opinfo_new->level = OPLOCK_EXCLUSIVE; + } +#else + if (req_oplock == SMB2_OPLOCK_LEVEL_BATCH) + opinfo_new->level = SMB2_OPLOCK_LEVEL_BATCH; + else + opinfo_new->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; +#endif + + if (lctx) { + lease->state = lctx->req_state; + memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); + } +} + +/** + * grant_read_oplock() - grant level2 oplock or read lease + * @opinfo_new: new oplock info object + * @lctx: lease context information + * + * Return: 0 + */ +static void grant_read_oplock(struct oplock_info *opinfo_new, + struct lease_ctx_info *lctx) +{ + struct lease *lease = opinfo_new->o_lease; + +#ifdef CONFIG_SMB_INSECURE_SERVER + if (opinfo_new->is_smb2) + opinfo_new->level = SMB2_OPLOCK_LEVEL_II; + else + opinfo_new->level = OPLOCK_READ; +#else + opinfo_new->level = SMB2_OPLOCK_LEVEL_II; +#endif + + if (lctx) { + lease->state = SMB2_LEASE_READ_CACHING_LE; + if (lctx->req_state & SMB2_LEASE_HANDLE_CACHING_LE) + lease->state |= SMB2_LEASE_HANDLE_CACHING_LE; + memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); + } +} + +/** + * grant_none_oplock() - grant none oplock or none lease + * @opinfo_new: new oplock info object + * @lctx: lease context information + * + * Return: 0 + */ +static void grant_none_oplock(struct oplock_info *opinfo_new, + struct lease_ctx_info *lctx) +{ + struct lease *lease = opinfo_new->o_lease; + +#ifdef CONFIG_SMB_INSECURE_SERVER + if (opinfo_new->is_smb2) + opinfo_new->level = SMB2_OPLOCK_LEVEL_NONE; + else + opinfo_new->level = OPLOCK_NONE; +#else + opinfo_new->level = SMB2_OPLOCK_LEVEL_NONE; +#endif + + if (lctx) { + lease->state = 0; + memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); + } +} + +static inline int compare_guid_key(struct oplock_info *opinfo, + const char *guid1, const char *key1) +{ + const char *guid2, *key2; + + guid2 = opinfo->conn->ClientGUID; + key2 = opinfo->o_lease->lease_key; + if (!memcmp(guid1, guid2, SMB2_CLIENT_GUID_SIZE) && + !memcmp(key1, key2, SMB2_LEASE_KEY_SIZE)) + return 1; + + return 0; +} + +/** + * same_client_has_lease() - check whether current lease request is + * from lease owner of file + * @ci: master file pointer + * @client_guid: Client GUID + * @lctx: lease context information + * + * Return: oplock(lease) object on success, otherwise NULL + */ +static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, + char *client_guid, + struct lease_ctx_info *lctx) +{ + int ret; + struct lease *lease; + struct oplock_info *opinfo; + struct oplock_info *m_opinfo = NULL; + + if (!lctx) + return NULL; + + /* + * Compare lease key and client_guid to know request from same owner + * of same client + */ + read_lock(&ci->m_lock); + list_for_each_entry(opinfo, &ci->m_op_list, op_entry) { + if (!opinfo->is_lease) + continue; + read_unlock(&ci->m_lock); + lease = opinfo->o_lease; + + ret = compare_guid_key(opinfo, client_guid, lctx->lease_key); + if (ret) { + m_opinfo = opinfo; + /* skip upgrading lease about breaking lease */ + if (atomic_read(&opinfo->breaking_cnt)) { + read_lock(&ci->m_lock); + continue; + } + + /* upgrading lease */ + if ((atomic_read(&ci->op_count) + + atomic_read(&ci->sop_count)) == 1) { + if (lease->state == + (lctx->req_state & lease->state)) { + lease->state |= lctx->req_state; + if (lctx->req_state & + SMB2_LEASE_WRITE_CACHING_LE) + lease_read_to_write(opinfo); + } + } else if ((atomic_read(&ci->op_count) + + atomic_read(&ci->sop_count)) > 1) { + if (lctx->req_state == + (SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE)) + lease->state = lctx->req_state; + } + + if (lctx->req_state && lease->state == + SMB2_LEASE_NONE_LE) + lease_none_upgrade(opinfo, lctx->req_state); + } + read_lock(&ci->m_lock); + } + read_unlock(&ci->m_lock); + + return m_opinfo; +} + +static void wait_for_break_ack(struct oplock_info *opinfo) +{ + int rc = 0; + + rc = wait_event_interruptible_timeout(opinfo->oplock_q, + opinfo->op_state == OPLOCK_STATE_NONE || + opinfo->op_state == OPLOCK_CLOSING, + OPLOCK_WAIT_TIME); + + /* is this a timeout ? */ + if (!rc) { + if (opinfo->is_lease) + opinfo->o_lease->state = SMB2_LEASE_NONE_LE; + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + opinfo->op_state = OPLOCK_STATE_NONE; + } +} + +static void wake_up_oplock_break(struct oplock_info *opinfo) +{ + clear_bit_unlock(0, &opinfo->pending_break); + /* memory barrier is needed for wake_up_bit() */ + smp_mb__after_atomic(); + wake_up_bit(&opinfo->pending_break, 0); +} + +static int oplock_break_pending(struct oplock_info *opinfo, int req_op_level) +{ + while (test_and_set_bit(0, &opinfo->pending_break)) { + wait_on_bit(&opinfo->pending_break, 0, TASK_UNINTERRUPTIBLE); + + /* Not immediately break to none. */ + opinfo->open_trunc = 0; + + if (opinfo->op_state == OPLOCK_CLOSING) + return -ENOENT; + else if (!opinfo->is_lease && opinfo->level <= req_op_level) + return 1; + } + + if (!opinfo->is_lease && opinfo->level <= req_op_level) { + wake_up_oplock_break(opinfo); + return 1; + } + return 0; +} + +static inline int allocate_oplock_break_buf(struct ksmbd_work *work) +{ + work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE, GFP_KERNEL); + if (!work->response_buf) + return -ENOMEM; + work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE; + return 0; +} + +#ifdef CONFIG_SMB_INSECURE_SERVER +/** + * smb1_oplock_break_noti() - send smb1 oplock break cmd from conn + * to client + * @work: smb work object + * + * There are two ways this function can be called. 1- while file open we break + * from exclusive/batch lock to levelII oplock and 2- while file write/truncate + * we break from levelII oplock no oplock. + * work->request_buf contains oplock_info. + */ +static void __smb1_oplock_break_noti(struct work_struct *wk) +{ + struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); + struct ksmbd_conn *conn = work->conn; + struct smb_hdr *rsp_hdr; + struct smb_com_lock_req *req; + struct oplock_info *opinfo = work->request_buf; + + if (allocate_oplock_break_buf(work)) { + pr_err("smb_allocate_rsp_buf failed! "); + ksmbd_free_work_struct(work); + return; + } + + /* Init response header */ + rsp_hdr = work->response_buf; + /* wct is 8 for locking andx(18) */ + memset(rsp_hdr, 0, sizeof(struct smb_hdr) + 18); + rsp_hdr->smb_buf_length = + cpu_to_be32(conn->vals->header_size - 4 + 18); + rsp_hdr->Protocol[0] = 0xFF; + rsp_hdr->Protocol[1] = 'S'; + rsp_hdr->Protocol[2] = 'M'; + rsp_hdr->Protocol[3] = 'B'; + + rsp_hdr->Command = SMB_COM_LOCKING_ANDX; + /* we know unicode, long file name and use nt error codes */ + rsp_hdr->Flags2 = SMBFLG2_UNICODE | SMBFLG2_KNOWS_LONG_NAMES | + SMBFLG2_ERR_STATUS; + rsp_hdr->Uid = cpu_to_le16(work->sess->id); + rsp_hdr->Pid = cpu_to_le16(0xFFFF); + rsp_hdr->Mid = cpu_to_le16(0xFFFF); + rsp_hdr->Tid = cpu_to_le16(opinfo->Tid); + rsp_hdr->WordCount = 8; + + /* Init locking request */ + req = work->response_buf; + + req->AndXCommand = 0xFF; + req->AndXReserved = 0; + req->AndXOffset = 0; + req->Fid = opinfo->fid; + req->LockType = LOCKING_ANDX_OPLOCK_RELEASE; + if (!opinfo->open_trunc && + (opinfo->level == OPLOCK_BATCH || + opinfo->level == OPLOCK_EXCLUSIVE)) + req->OplockLevel = 1; + else + req->OplockLevel = 0; + req->Timeout = 0; + req->NumberOfUnlocks = 0; + req->ByteCount = 0; + ksmbd_debug(OPLOCK, "sending oplock break for fid %d lock level = %d\n", + req->Fid, req->OplockLevel); + + ksmbd_conn_write(work); + ksmbd_free_work_struct(work); + /* + * Checking waitqueue to dropping pending requests on + * disconnection. waitqueue_active is safe because it + * uses atomic operation for condition. + */ + if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q)) + wake_up(&conn->r_count_q); +} + +/** + * smb1_oplock_break() - send smb1 exclusive/batch to level2 oplock + * break command from server to client + * @opinfo: oplock info object + * @ack_required if requiring ack + * + * Return: 0 on success, otherwise error + */ +static int smb1_oplock_break_noti(struct oplock_info *opinfo) +{ + struct ksmbd_conn *conn = opinfo->conn; + struct ksmbd_work *work = ksmbd_alloc_work_struct(); + + if (!work) + return -ENOMEM; + + work->request_buf = (char *)opinfo; + work->conn = conn; + + atomic_inc(&conn->r_count); + if (opinfo->op_state == OPLOCK_ACK_WAIT) { + INIT_WORK(&work->work, __smb1_oplock_break_noti); + ksmbd_queue_work(work); + + wait_for_break_ack(opinfo); + } else { + __smb1_oplock_break_noti(&work->work); + if (opinfo->level == OPLOCK_READ) + opinfo->level = OPLOCK_NONE; + } + return 0; +} +#endif + +/** + * __smb2_oplock_break_noti() - send smb2 oplock break cmd from conn + * to client + * @wk: smb work object + * + * There are two ways this function can be called. 1- while file open we break + * from exclusive/batch lock to levelII oplock and 2- while file write/truncate + * we break from levelII oplock no oplock. + * work->request_buf contains oplock_info. + */ +static void __smb2_oplock_break_noti(struct work_struct *wk) +{ + struct smb2_oplock_break *rsp = NULL; + struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); + struct ksmbd_conn *conn = work->conn; + struct oplock_break_info *br_info = work->request_buf; + struct smb2_hdr *rsp_hdr; + struct ksmbd_file *fp; + + fp = ksmbd_lookup_durable_fd(br_info->fid); + if (!fp) + goto out; + + if (allocate_oplock_break_buf(work)) { + pr_err("smb2_allocate_rsp_buf failed! "); + ksmbd_fd_put(work, fp); + goto out; + } + + rsp_hdr = smb2_get_msg(work->response_buf); + memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); + *(__be32 *)work->response_buf = + cpu_to_be32(conn->vals->header_size); + rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->CreditRequest = cpu_to_le16(0); + rsp_hdr->Command = SMB2_OPLOCK_BREAK; + rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); + rsp_hdr->NextCommand = 0; + rsp_hdr->MessageId = cpu_to_le64(-1); + rsp_hdr->Id.SyncId.ProcessId = 0; + rsp_hdr->Id.SyncId.TreeId = 0; + rsp_hdr->SessionId = 0; + memset(rsp_hdr->Signature, 0, 16); + + rsp = smb2_get_msg(work->response_buf); + + rsp->StructureSize = cpu_to_le16(24); + if (!br_info->open_trunc && + (br_info->level == SMB2_OPLOCK_LEVEL_BATCH || + br_info->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) + rsp->OplockLevel = SMB2_OPLOCK_LEVEL_II; + else + rsp->OplockLevel = SMB2_OPLOCK_LEVEL_NONE; + rsp->Reserved = 0; + rsp->Reserved2 = 0; + rsp->PersistentFid = cpu_to_le64(fp->persistent_id); + rsp->VolatileFid = cpu_to_le64(fp->volatile_id); + + inc_rfc1001_len(work->response_buf, 24); + + ksmbd_debug(OPLOCK, + "sending oplock break v_id %llu p_id = %llu lock level = %d\n", + rsp->VolatileFid, rsp->PersistentFid, rsp->OplockLevel); + + ksmbd_fd_put(work, fp); + ksmbd_conn_write(work); + +out: + ksmbd_free_work_struct(work); + /* + * Checking waitqueue to dropping pending requests on + * disconnection. waitqueue_active is safe because it + * uses atomic operation for condition. + */ + if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q)) + wake_up(&conn->r_count_q); +} + +/** + * smb2_oplock_break_noti() - send smb2 exclusive/batch to level2 oplock + * break command from server to client + * @opinfo: oplock info object + * + * Return: 0 on success, otherwise error + */ +static int smb2_oplock_break_noti(struct oplock_info *opinfo) +{ + struct ksmbd_conn *conn = opinfo->conn; + struct oplock_break_info *br_info; + int ret = 0; + struct ksmbd_work *work = ksmbd_alloc_work_struct(); + + if (!work) + return -ENOMEM; + + br_info = kmalloc(sizeof(struct oplock_break_info), GFP_KERNEL); + if (!br_info) { + ksmbd_free_work_struct(work); + return -ENOMEM; + } + + br_info->level = opinfo->level; + br_info->fid = opinfo->fid; + br_info->open_trunc = opinfo->open_trunc; + + work->request_buf = (char *)br_info; + work->conn = conn; + work->sess = opinfo->sess; + + atomic_inc(&conn->r_count); + if (opinfo->op_state == OPLOCK_ACK_WAIT) { + INIT_WORK(&work->work, __smb2_oplock_break_noti); + ksmbd_queue_work(work); + + wait_for_break_ack(opinfo); + } else { + __smb2_oplock_break_noti(&work->work); + if (opinfo->level == SMB2_OPLOCK_LEVEL_II) + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + } + return ret; +} + +/** + * __smb2_lease_break_noti() - send lease break command from server + * to client + * @wk: smb work object + */ +static void __smb2_lease_break_noti(struct work_struct *wk) +{ + struct smb2_lease_break *rsp = NULL; + struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); + struct lease_break_info *br_info = work->request_buf; + struct ksmbd_conn *conn = work->conn; + struct smb2_hdr *rsp_hdr; + + if (allocate_oplock_break_buf(work)) { + ksmbd_debug(OPLOCK, "smb2_allocate_rsp_buf failed! "); + goto out; + } + + rsp_hdr = smb2_get_msg(work->response_buf); + memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); + *(__be32 *)work->response_buf = + cpu_to_be32(conn->vals->header_size); + rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->CreditRequest = cpu_to_le16(0); + rsp_hdr->Command = SMB2_OPLOCK_BREAK; + rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); + rsp_hdr->NextCommand = 0; + rsp_hdr->MessageId = cpu_to_le64(-1); + rsp_hdr->Id.SyncId.ProcessId = 0; + rsp_hdr->Id.SyncId.TreeId = 0; + rsp_hdr->SessionId = 0; + memset(rsp_hdr->Signature, 0, 16); + + rsp = smb2_get_msg(work->response_buf); + rsp->StructureSize = cpu_to_le16(44); + rsp->Epoch = br_info->epoch; + rsp->Flags = 0; + + if (br_info->curr_state & (SMB2_LEASE_WRITE_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE)) + rsp->Flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED; + + memcpy(rsp->LeaseKey, br_info->lease_key, SMB2_LEASE_KEY_SIZE); + rsp->CurrentLeaseState = br_info->curr_state; + rsp->NewLeaseState = br_info->new_state; + rsp->BreakReason = 0; + rsp->AccessMaskHint = 0; + rsp->ShareMaskHint = 0; + + inc_rfc1001_len(work->response_buf, 44); + + ksmbd_conn_write(work); + +out: + ksmbd_free_work_struct(work); + /* + * Checking waitqueue to dropping pending requests on + * disconnection. waitqueue_active is safe because it + * uses atomic operation for condition. + */ + if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q)) + wake_up(&conn->r_count_q); +} + +/** + * smb2_lease_break_noti() - break lease when a new client request + * write lease + * @opinfo: conains lease state information + * + * Return: 0 on success, otherwise error + */ +static int smb2_lease_break_noti(struct oplock_info *opinfo) +{ + struct ksmbd_conn *conn = opinfo->conn; + struct list_head *tmp, *t; + struct ksmbd_work *work; + struct lease_break_info *br_info; + struct lease *lease = opinfo->o_lease; + + work = ksmbd_alloc_work_struct(); + if (!work) + return -ENOMEM; + + br_info = kmalloc(sizeof(struct lease_break_info), GFP_KERNEL); + if (!br_info) { + ksmbd_free_work_struct(work); + return -ENOMEM; + } + + br_info->curr_state = lease->state; + br_info->new_state = lease->new_state; + if (lease->version == 2) + br_info->epoch = cpu_to_le16(++lease->epoch); + else + br_info->epoch = 0; + memcpy(br_info->lease_key, lease->lease_key, SMB2_LEASE_KEY_SIZE); + + work->request_buf = (char *)br_info; + work->conn = conn; + work->sess = opinfo->sess; + + atomic_inc(&conn->r_count); + if (opinfo->op_state == OPLOCK_ACK_WAIT) { + list_for_each_safe(tmp, t, &opinfo->interim_list) { + struct ksmbd_work *in_work; + + in_work = list_entry(tmp, struct ksmbd_work, + interim_entry); + setup_async_work(in_work, NULL, NULL); + smb2_send_interim_resp(in_work, STATUS_PENDING); + list_del(&in_work->interim_entry); + } + INIT_WORK(&work->work, __smb2_lease_break_noti); + ksmbd_queue_work(work); + wait_for_break_ack(opinfo); + } else { + __smb2_lease_break_noti(&work->work); + if (opinfo->o_lease->new_state == SMB2_LEASE_NONE_LE) { + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + opinfo->o_lease->state = SMB2_LEASE_NONE_LE; + } + } + return 0; +} + +static void wait_lease_breaking(struct oplock_info *opinfo) +{ + if (!opinfo->is_lease) + return; + + wake_up_interruptible_all(&opinfo->oplock_brk); + if (atomic_read(&opinfo->breaking_cnt)) { + int ret = 0; + + ret = wait_event_interruptible_timeout(opinfo->oplock_brk, + atomic_read(&opinfo->breaking_cnt) == 0, + HZ); + if (!ret) + atomic_set(&opinfo->breaking_cnt, 0); + } +} + +static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level) +{ + int err = 0; + + /* Need to break exclusive/batch oplock, write lease or overwrite_if */ + ksmbd_debug(OPLOCK, + "request to send oplock(level : 0x%x) break notification\n", + brk_opinfo->level); + + if (brk_opinfo->is_lease) { + struct lease *lease = brk_opinfo->o_lease; + + atomic_inc(&brk_opinfo->breaking_cnt); + + err = oplock_break_pending(brk_opinfo, req_op_level); + if (err) + return err < 0 ? err : 0; + + if (brk_opinfo->open_trunc) { + /* + * Create overwrite break trigger the lease break to + * none. + */ + lease->new_state = SMB2_LEASE_NONE_LE; + } else { + if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) { + if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) + lease->new_state = + SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE; + else + lease->new_state = + SMB2_LEASE_READ_CACHING_LE; + } else { + if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) + lease->new_state = + SMB2_LEASE_READ_CACHING_LE; + else + lease->new_state = SMB2_LEASE_NONE_LE; + } + } + + if (lease->state & (SMB2_LEASE_WRITE_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE)) + brk_opinfo->op_state = OPLOCK_ACK_WAIT; + else + atomic_dec(&brk_opinfo->breaking_cnt); + } else { + err = oplock_break_pending(brk_opinfo, req_op_level); + if (err) + return err < 0 ? err : 0; + + if (brk_opinfo->level == SMB2_OPLOCK_LEVEL_BATCH || + brk_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) + brk_opinfo->op_state = OPLOCK_ACK_WAIT; + } + +#ifdef CONFIG_SMB_INSECURE_SERVER + if (brk_opinfo->is_smb2) + if (brk_opinfo->is_lease) + err = smb2_lease_break_noti(brk_opinfo); + else + err = smb2_oplock_break_noti(brk_opinfo); + else + err = smb1_oplock_break_noti(brk_opinfo); +#else + if (brk_opinfo->is_lease) + err = smb2_lease_break_noti(brk_opinfo); + else + err = smb2_oplock_break_noti(brk_opinfo); +#endif + + ksmbd_debug(OPLOCK, "oplock granted = %d\n", brk_opinfo->level); + if (brk_opinfo->op_state == OPLOCK_CLOSING) + err = -ENOENT; + wake_up_oplock_break(brk_opinfo); + + wait_lease_breaking(brk_opinfo); + + return err; +} + +void destroy_lease_table(struct ksmbd_conn *conn) +{ + struct lease_table *lb, *lbtmp; + struct oplock_info *opinfo; + + write_lock(&lease_list_lock); + if (list_empty(&lease_table_list)) { + write_unlock(&lease_list_lock); + return; + } + + list_for_each_entry_safe(lb, lbtmp, &lease_table_list, l_entry) { + if (conn && memcmp(lb->client_guid, conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE)) + continue; +again: + rcu_read_lock(); + list_for_each_entry_rcu(opinfo, &lb->lease_list, + lease_entry) { + rcu_read_unlock(); + lease_del_list(opinfo); + goto again; + } + rcu_read_unlock(); + list_del(&lb->l_entry); + kfree(lb); + } + write_unlock(&lease_list_lock); +} + +int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, + struct lease_ctx_info *lctx) +{ + struct oplock_info *opinfo; + int err = 0; + struct lease_table *lb; + + if (!lctx) + return err; + + read_lock(&lease_list_lock); + if (list_empty(&lease_table_list)) { + read_unlock(&lease_list_lock); + return 0; + } + + list_for_each_entry(lb, &lease_table_list, l_entry) { + if (!memcmp(lb->client_guid, sess->ClientGUID, + SMB2_CLIENT_GUID_SIZE)) + goto found; + } + read_unlock(&lease_list_lock); + + return 0; + +found: + rcu_read_lock(); + list_for_each_entry_rcu(opinfo, &lb->lease_list, lease_entry) { + if (!atomic_inc_not_zero(&opinfo->refcount)) + continue; + rcu_read_unlock(); + if (opinfo->o_fp->f_ci == ci) + goto op_next; + err = compare_guid_key(opinfo, sess->ClientGUID, + lctx->lease_key); + if (err) { + err = -EINVAL; + ksmbd_debug(OPLOCK, + "found same lease key is already used in other files\n"); + opinfo_put(opinfo); + goto out; + } +op_next: + opinfo_put(opinfo); + rcu_read_lock(); + } + rcu_read_unlock(); + +out: + read_unlock(&lease_list_lock); + return err; +} + +static void copy_lease(struct oplock_info *op1, struct oplock_info *op2) +{ + struct lease *lease1 = op1->o_lease; + struct lease *lease2 = op2->o_lease; + + op2->level = op1->level; + lease2->state = lease1->state; + memcpy(lease2->lease_key, lease1->lease_key, + SMB2_LEASE_KEY_SIZE); + lease2->duration = lease1->duration; + lease2->flags = lease1->flags; +} + +static int add_lease_global_list(struct oplock_info *opinfo) +{ + struct lease_table *lb; + + read_lock(&lease_list_lock); + list_for_each_entry(lb, &lease_table_list, l_entry) { + if (!memcmp(lb->client_guid, opinfo->conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE)) { + opinfo->o_lease->l_lb = lb; + lease_add_list(opinfo); + read_unlock(&lease_list_lock); + return 0; + } + } + read_unlock(&lease_list_lock); + + lb = kmalloc(sizeof(struct lease_table), GFP_KERNEL); + if (!lb) + return -ENOMEM; + + memcpy(lb->client_guid, opinfo->conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE); + INIT_LIST_HEAD(&lb->lease_list); + spin_lock_init(&lb->lb_lock); + opinfo->o_lease->l_lb = lb; + lease_add_list(opinfo); + lb_add(lb); + return 0; +} + +static void set_oplock_level(struct oplock_info *opinfo, int level, + struct lease_ctx_info *lctx) +{ + switch (level) { +#ifdef CONFIG_SMB_INSECURE_SERVER + case REQ_OPLOCK: + case REQ_BATCHOPLOCK: +#endif + case SMB2_OPLOCK_LEVEL_BATCH: + case SMB2_OPLOCK_LEVEL_EXCLUSIVE: + grant_write_oplock(opinfo, level, lctx); + break; + case SMB2_OPLOCK_LEVEL_II: + grant_read_oplock(opinfo, lctx); + break; + default: + grant_none_oplock(opinfo, lctx); + break; + } +} + +/** + * smb_grant_oplock() - handle oplock/lease request on file open + * @work: smb work + * @req_op_level: oplock level + * @pid: id of open file + * @fp: ksmbd file pointer + * @tid: Tree id of connection + * @lctx: lease context information on file open + * @share_ret: share mode + * + * Return: 0 on success, otherwise error + */ +int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, + struct ksmbd_file *fp, __u16 tid, + struct lease_ctx_info *lctx, int share_ret) +{ + struct ksmbd_session *sess = work->sess; + int err = 0; + struct oplock_info *opinfo = NULL, *prev_opinfo = NULL; + struct ksmbd_inode *ci = fp->f_ci; + bool prev_op_has_lease; + __le32 prev_op_state = 0; + + /* not support directory lease */ + if (S_ISDIR(file_inode(fp->filp)->i_mode)) + return 0; + + opinfo = alloc_opinfo(work, pid, tid); + if (!opinfo) + return -ENOMEM; + + if (lctx) { + err = alloc_lease(opinfo, lctx); + if (err) + goto err_out; + opinfo->is_lease = 1; + } + + /* ci does not have any oplock */ + if (!opinfo_count(fp)) + goto set_lev; + + /* grant none-oplock if second open is trunc */ + if (fp->attrib_only && fp->cdoption != FILE_OVERWRITE_IF_LE && + fp->cdoption != FILE_OVERWRITE_LE && + fp->cdoption != FILE_SUPERSEDE_LE) { + req_op_level = SMB2_OPLOCK_LEVEL_NONE; + goto set_lev; + } + + if (lctx) { + struct oplock_info *m_opinfo; + + /* is lease already granted ? */ + m_opinfo = same_client_has_lease(ci, sess->ClientGUID, + lctx); + if (m_opinfo) { + copy_lease(m_opinfo, opinfo); + if (atomic_read(&m_opinfo->breaking_cnt)) + opinfo->o_lease->flags = + SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE; + goto out; + } + } + prev_opinfo = opinfo_get_list(ci); + if (!prev_opinfo || + (prev_opinfo->level == SMB2_OPLOCK_LEVEL_NONE && lctx)) + goto set_lev; + prev_op_has_lease = prev_opinfo->is_lease; + if (prev_op_has_lease) + prev_op_state = prev_opinfo->o_lease->state; + + if (share_ret < 0 && + prev_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) { + err = share_ret; + opinfo_put(prev_opinfo); + goto err_out; + } + + if (prev_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH && + prev_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) { + opinfo_put(prev_opinfo); + goto op_break_not_needed; + } + + list_add(&work->interim_entry, &prev_opinfo->interim_list); + err = oplock_break(prev_opinfo, SMB2_OPLOCK_LEVEL_II); + opinfo_put(prev_opinfo); + if (err == -ENOENT) + goto set_lev; + /* Check all oplock was freed by close */ + else if (err < 0) + goto err_out; + +op_break_not_needed: + if (share_ret < 0) { + err = share_ret; + goto err_out; + } + + if (req_op_level != SMB2_OPLOCK_LEVEL_NONE) + req_op_level = SMB2_OPLOCK_LEVEL_II; + + /* grant fixed oplock on stacked locking between lease and oplock */ + if (prev_op_has_lease && !lctx) + if (prev_op_state & SMB2_LEASE_HANDLE_CACHING_LE) + req_op_level = SMB2_OPLOCK_LEVEL_NONE; + + if (!prev_op_has_lease && lctx) { + req_op_level = SMB2_OPLOCK_LEVEL_II; + lctx->req_state = SMB2_LEASE_READ_CACHING_LE; + } + +set_lev: + set_oplock_level(opinfo, req_op_level, lctx); + +out: + rcu_assign_pointer(fp->f_opinfo, opinfo); + opinfo->o_fp = fp; + + opinfo_count_inc(fp); + opinfo_add(opinfo); + if (opinfo->is_lease) { + err = add_lease_global_list(opinfo); + if (err) + goto err_out; + } + + return 0; +err_out: + free_opinfo(opinfo); + return err; +} + +/** + * smb_break_all_write_oplock() - break batch/exclusive oplock to level2 + * @work: smb work + * @fp: ksmbd file pointer + * @is_trunc: truncate on open + */ +static void smb_break_all_write_oplock(struct ksmbd_work *work, + struct ksmbd_file *fp, int is_trunc) +{ + struct oplock_info *brk_opinfo; + + brk_opinfo = opinfo_get_list(fp->f_ci); + if (!brk_opinfo) + return; + if (brk_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH && + brk_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) { + opinfo_put(brk_opinfo); + return; + } + + brk_opinfo->open_trunc = is_trunc; + list_add(&work->interim_entry, &brk_opinfo->interim_list); + oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II); + opinfo_put(brk_opinfo); +} + +/** + * smb_break_all_levII_oplock() - send level2 oplock or read lease break command + * from server to client + * @work: smb work + * @fp: ksmbd file pointer + * @is_trunc: truncate on open + */ +void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, + int is_trunc) +{ + struct oplock_info *op, *brk_op; + struct ksmbd_inode *ci; + struct ksmbd_conn *conn = work->conn; + + if (!test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_OPLOCKS)) + return; + + ci = fp->f_ci; + op = opinfo_get(fp); + + rcu_read_lock(); + list_for_each_entry_rcu(brk_op, &ci->m_op_list, op_entry) { + if (!atomic_inc_not_zero(&brk_op->refcount)) + continue; + rcu_read_unlock(); + +#ifdef CONFIG_SMB_INSECURE_SERVER + if (brk_op->is_smb2) { + if (brk_op->is_lease && (brk_op->o_lease->state & + (~(SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE)))) { + ksmbd_debug(OPLOCK, + "unexpected lease state(0x%x)\n", + brk_op->o_lease->state); + goto next; + } else if (brk_op->level != + SMB2_OPLOCK_LEVEL_II) { + ksmbd_debug(OPLOCK, "unexpected oplock(0x%x)\n", + brk_op->level); + goto next; + } + + /* Skip oplock being break to none */ + if (brk_op->is_lease && + brk_op->o_lease->new_state == SMB2_LEASE_NONE_LE && + atomic_read(&brk_op->breaking_cnt)) + goto next; + } else { + if (brk_op->level != OPLOCK_READ) { + ksmbd_debug(OPLOCK, "unexpected oplock(0x%x)\n", + brk_op->level); + goto next; + } + } +#else + if (brk_op->is_lease && + (brk_op->o_lease->state & + (~(SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE)))) { + ksmbd_debug(OPLOCK, "unexpected lease state(0x%x)\n", + brk_op->o_lease->state); + goto next; + } else if (brk_op->level != SMB2_OPLOCK_LEVEL_II) { + ksmbd_debug(OPLOCK, "unexpected oplock(0x%x)\n", + brk_op->level); + goto next; + } + + /* Skip oplock being break to none */ + if (brk_op->is_lease && + brk_op->o_lease->new_state == SMB2_LEASE_NONE_LE && + atomic_read(&brk_op->breaking_cnt)) + goto next; +#endif + + if (op && op->is_lease && brk_op->is_lease && + !memcmp(conn->ClientGUID, brk_op->conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE) && + !memcmp(op->o_lease->lease_key, brk_op->o_lease->lease_key, + SMB2_LEASE_KEY_SIZE)) + goto next; + brk_op->open_trunc = is_trunc; + oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE); +next: + opinfo_put(brk_op); + rcu_read_lock(); + } + rcu_read_unlock(); + + if (op) + opinfo_put(op); +} + +/** + * smb_break_all_oplock() - break both batch/exclusive and level2 oplock + * @work: smb work + * @fp: ksmbd file pointer + */ +void smb_break_all_oplock(struct ksmbd_work *work, struct ksmbd_file *fp) +{ + if (!test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_OPLOCKS)) + return; + + smb_break_all_write_oplock(work, fp, 1); + smb_break_all_levII_oplock(work, fp, 1); +} + +/** + * smb2_map_lease_to_oplock() - map lease state to corresponding oplock type + * @lease_state: lease type + * + * Return: 0 if no mapping, otherwise corresponding oplock type + */ +__u8 smb2_map_lease_to_oplock(__le32 lease_state) +{ + if (lease_state == (SMB2_LEASE_HANDLE_CACHING_LE | + SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_WRITE_CACHING_LE)) { + return SMB2_OPLOCK_LEVEL_BATCH; + } else if (lease_state != SMB2_LEASE_WRITE_CACHING_LE && + lease_state & SMB2_LEASE_WRITE_CACHING_LE) { + if (!(lease_state & SMB2_LEASE_HANDLE_CACHING_LE)) + return SMB2_OPLOCK_LEVEL_EXCLUSIVE; + } else if (lease_state & SMB2_LEASE_READ_CACHING_LE) { + return SMB2_OPLOCK_LEVEL_II; + } + return 0; +} + +/** + * create_lease_buf() - create lease context for open cmd response + * @rbuf: buffer to create lease context response + * @lease: buffer to stored parsed lease state information + */ +void create_lease_buf(u8 *rbuf, struct lease *lease) +{ + if (lease->version == 2) { + struct create_lease_v2 *buf = (struct create_lease_v2 *)rbuf; + + memset(buf, 0, sizeof(struct create_lease_v2)); + memcpy(buf->lcontext.LeaseKey, lease->lease_key, + SMB2_LEASE_KEY_SIZE); + buf->lcontext.LeaseFlags = lease->flags; + buf->lcontext.LeaseState = lease->state; + memcpy(buf->lcontext.ParentLeaseKey, lease->parent_lease_key, + SMB2_LEASE_KEY_SIZE); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_lease_v2, lcontext)); + buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context_v2)); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_lease_v2, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + buf->Name[0] = 'R'; + buf->Name[1] = 'q'; + buf->Name[2] = 'L'; + buf->Name[3] = 's'; + } else { + struct create_lease *buf = (struct create_lease *)rbuf; + + memset(buf, 0, sizeof(struct create_lease)); + memcpy(buf->lcontext.LeaseKey, lease->lease_key, SMB2_LEASE_KEY_SIZE); + buf->lcontext.LeaseFlags = lease->flags; + buf->lcontext.LeaseState = lease->state; + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_lease, lcontext)); + buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context)); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_lease, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + buf->Name[0] = 'R'; + buf->Name[1] = 'q'; + buf->Name[2] = 'L'; + buf->Name[3] = 's'; + } +} + +/** + * parse_lease_state() - parse lease context containted in file open request + * @open_req: buffer containing smb2 file open(create) request + * + * Return: oplock state, -ENOENT if create lease context not found + */ +struct lease_ctx_info *parse_lease_state(void *open_req) +{ + char *data_offset; + struct create_context *cc; + unsigned int next = 0; + char *name; + bool found = false; + struct smb2_create_req *req = (struct smb2_create_req *)open_req; + struct lease_ctx_info *lreq = kzalloc(sizeof(struct lease_ctx_info), + GFP_KERNEL); + if (!lreq) + return NULL; + + data_offset = (char *)req + le32_to_cpu(req->CreateContextsOffset); + cc = (struct create_context *)data_offset; + do { + cc = (struct create_context *)((char *)cc + next); + name = le16_to_cpu(cc->NameOffset) + (char *)cc; + if (le16_to_cpu(cc->NameLength) != 4 || + strncmp(name, SMB2_CREATE_REQUEST_LEASE, 4)) { + next = le32_to_cpu(cc->Next); + continue; + } + found = true; + break; + } while (next != 0); + + if (found) { + if (sizeof(struct lease_context_v2) == le32_to_cpu(cc->DataLength)) { + struct create_lease_v2 *lc = (struct create_lease_v2 *)cc; + + memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); + lreq->req_state = lc->lcontext.LeaseState; + lreq->flags = lc->lcontext.LeaseFlags; + lreq->duration = lc->lcontext.LeaseDuration; + memcpy(lreq->parent_lease_key, lc->lcontext.ParentLeaseKey, + SMB2_LEASE_KEY_SIZE); + lreq->version = 2; + } else { + struct create_lease *lc = (struct create_lease *)cc; + + memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); + lreq->req_state = lc->lcontext.LeaseState; + lreq->flags = lc->lcontext.LeaseFlags; + lreq->duration = lc->lcontext.LeaseDuration; + lreq->version = 1; + } + return lreq; + } + + kfree(lreq); + return NULL; +} + +/** + * smb2_find_context_vals() - find a particular context info in open request + * @open_req: buffer containing smb2 file open(create) request + * @tag: context name to search for + * + * Return: pointer to requested context, NULL if @str context not found + * or error pointer if name length is invalid. + */ +struct create_context *smb2_find_context_vals(void *open_req, const char *tag) +{ + struct create_context *cc; + unsigned int next = 0; + char *name; + struct smb2_create_req *req = (struct smb2_create_req *)open_req; + unsigned int remain_len, name_off, name_len, value_off, value_len, + cc_len; + + /* + * CreateContextsOffset and CreateContextsLength are guaranteed to + * be valid because of ksmbd_smb2_check_message(). + */ + cc = (struct create_context *)((char *)req + + le32_to_cpu(req->CreateContextsOffset)); + remain_len = le32_to_cpu(req->CreateContextsLength); + do { + cc = (struct create_context *)((char *)cc + next); + if (remain_len < offsetof(struct create_context, Buffer)) + return ERR_PTR(-EINVAL); + + next = le32_to_cpu(cc->Next); + name_off = le16_to_cpu(cc->NameOffset); + name_len = le16_to_cpu(cc->NameLength); + value_off = le16_to_cpu(cc->DataOffset); + value_len = le32_to_cpu(cc->DataLength); + cc_len = next ? next : remain_len; + + if ((next & 0x7) != 0 || + next > remain_len || + name_off != offsetof(struct create_context, Buffer) || + name_len < 4 || + name_off + name_len > cc_len || + (value_off & 0x7) != 0 || + (value_off && (value_off < name_off + name_len)) || + ((u64)value_off + value_len > cc_len)) + return ERR_PTR(-EINVAL); + + name = (char *)cc + name_off; + if (memcmp(name, tag, name_len) == 0) + return cc; + + remain_len -= next; + } while (next != 0); + + return NULL; +} + +/** + * create_durable_rsp_buf() - create durable handle context + * @cc: buffer to create durable context response + */ +void create_durable_rsp_buf(char *cc) +{ + struct create_durable_rsp *buf; + + buf = (struct create_durable_rsp *)cc; + memset(buf, 0, sizeof(struct create_durable_rsp)); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_durable_rsp, Data)); + buf->ccontext.DataLength = cpu_to_le32(8); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_durable_rsp, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_DURABLE_HANDLE_RESPONSE is "DHnQ" */ + buf->Name[0] = 'D'; + buf->Name[1] = 'H'; + buf->Name[2] = 'n'; + buf->Name[3] = 'Q'; +} + +/** + * create_durable_v2_rsp_buf() - create durable handle v2 context + * @cc: buffer to create durable context response + * @fp: ksmbd file pointer + */ +void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp) +{ + struct create_durable_v2_rsp *buf; + + buf = (struct create_durable_v2_rsp *)cc; + memset(buf, 0, sizeof(struct create_durable_rsp)); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_durable_rsp, Data)); + buf->ccontext.DataLength = cpu_to_le32(8); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_durable_rsp, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_DURABLE_HANDLE_RESPONSE_V2 is "DH2Q" */ + buf->Name[0] = 'D'; + buf->Name[1] = 'H'; + buf->Name[2] = '2'; + buf->Name[3] = 'Q'; + + buf->Timeout = cpu_to_le32(fp->durable_timeout); +} + +/** + * create_mxac_rsp_buf() - create query maximal access context + * @cc: buffer to create maximal access context response + * @maximal_access: maximal access + */ +void create_mxac_rsp_buf(char *cc, int maximal_access) +{ + struct create_mxac_rsp *buf; + + buf = (struct create_mxac_rsp *)cc; + memset(buf, 0, sizeof(struct create_mxac_rsp)); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_mxac_rsp, QueryStatus)); + buf->ccontext.DataLength = cpu_to_le32(8); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_mxac_rsp, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_QUERY_MAXIMAL_ACCESS_RESPONSE is "MxAc" */ + buf->Name[0] = 'M'; + buf->Name[1] = 'x'; + buf->Name[2] = 'A'; + buf->Name[3] = 'c'; + + buf->QueryStatus = STATUS_SUCCESS; + buf->MaximalAccess = cpu_to_le32(maximal_access); +} + +void create_disk_id_rsp_buf(char *cc, __u64 file_id, __u64 vol_id) +{ + struct create_disk_id_rsp *buf; + + buf = (struct create_disk_id_rsp *)cc; + memset(buf, 0, sizeof(struct create_disk_id_rsp)); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_disk_id_rsp, DiskFileId)); + buf->ccontext.DataLength = cpu_to_le32(32); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_mxac_rsp, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + /* SMB2_CREATE_QUERY_ON_DISK_ID_RESPONSE is "QFid" */ + buf->Name[0] = 'Q'; + buf->Name[1] = 'F'; + buf->Name[2] = 'i'; + buf->Name[3] = 'd'; + + buf->DiskFileId = cpu_to_le64(file_id); + buf->VolumeId = cpu_to_le64(vol_id); +} + +/** + * create_posix_rsp_buf() - create posix extension context + * @cc: buffer to create posix on posix response + * @fp: ksmbd file pointer + */ +void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp) +{ + struct create_posix_rsp *buf; + struct inode *inode = file_inode(fp->filp); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + struct user_namespace *user_ns = file_mnt_user_ns(fp->filp); +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + vfsuid_t vfsuid = i_uid_into_vfsuid(user_ns, inode); + vfsgid_t vfsgid = i_gid_into_vfsgid(user_ns, inode); +#endif + + buf = (struct create_posix_rsp *)cc; + memset(buf, 0, sizeof(struct create_posix_rsp)); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_posix_rsp, nlink)); + /* + * DataLength = nlink(4) + reparse_tag(4) + mode(4) + + * domain sid(28) + unix group sid(16). + */ + buf->ccontext.DataLength = cpu_to_le32(56); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_posix_rsp, Name)); + buf->ccontext.NameLength = cpu_to_le16(POSIX_CTXT_DATA_LEN); + /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ + buf->Name[0] = 0x93; + buf->Name[1] = 0xAD; + buf->Name[2] = 0x25; + buf->Name[3] = 0x50; + buf->Name[4] = 0x9C; + buf->Name[5] = 0xB4; + buf->Name[6] = 0x11; + buf->Name[7] = 0xE7; + buf->Name[8] = 0xB4; + buf->Name[9] = 0x23; + buf->Name[10] = 0x83; + buf->Name[11] = 0xDE; + buf->Name[12] = 0x96; + buf->Name[13] = 0x8B; + buf->Name[14] = 0xCD; + buf->Name[15] = 0x7C; + + buf->nlink = cpu_to_le32(inode->i_nlink); + buf->reparse_tag = cpu_to_le32(fp->volatile_id); + buf->mode = cpu_to_le32(inode->i_mode & 0777); + /* + * SidBuffer(44) contain two sids(Domain sid(28), UNIX group sid(16)). + * Domain sid(28) = revision(1) + num_subauth(1) + authority(6) + + * sub_auth(4 * 4(num_subauth)) + RID(4). + * UNIX group id(16) = revision(1) + num_subauth(1) + authority(6) + + * sub_auth(4 * 1(num_subauth)) + RID(4). + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + id_to_sid(from_kuid_munged(&init_user_ns, vfsuid_into_kuid(vfsuid)), +#else + id_to_sid(from_kuid_munged(&init_user_ns, + i_uid_into_mnt(user_ns, inode)), +#endif +#else + id_to_sid(from_kuid_munged(&init_user_ns, inode->i_uid), +#endif + SIDOWNER, (struct smb_sid *)&buf->SidBuffer[0]); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + id_to_sid(from_kgid_munged(&init_user_ns, vfsgid_into_kgid(vfsgid)), +#else + id_to_sid(from_kgid_munged(&init_user_ns, + i_gid_into_mnt(user_ns, inode)), +#endif +#else + id_to_sid(from_kgid_munged(&init_user_ns, inode->i_gid), +#endif + SIDUNIX_GROUP, (struct smb_sid *)&buf->SidBuffer[28]); +} + +/* + * Find lease object(opinfo) for given lease key/fid from lease + * break/file close path. + */ +/** + * lookup_lease_in_table() - find a matching lease info object + * @conn: connection instance + * @lease_key: lease key to be searched for + * + * Return: opinfo if found matching opinfo, otherwise NULL + */ +struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, + char *lease_key) +{ + struct oplock_info *opinfo = NULL, *ret_op = NULL; + struct lease_table *lt; + int ret; + + read_lock(&lease_list_lock); + list_for_each_entry(lt, &lease_table_list, l_entry) { + if (!memcmp(lt->client_guid, conn->ClientGUID, + SMB2_CLIENT_GUID_SIZE)) + goto found; + } + + read_unlock(&lease_list_lock); + return NULL; + +found: + rcu_read_lock(); + list_for_each_entry_rcu(opinfo, <->lease_list, lease_entry) { + if (!atomic_inc_not_zero(&opinfo->refcount)) + continue; + rcu_read_unlock(); + if (!opinfo->op_state || opinfo->op_state == OPLOCK_CLOSING) + goto op_next; + if (!(opinfo->o_lease->state & + (SMB2_LEASE_HANDLE_CACHING_LE | + SMB2_LEASE_WRITE_CACHING_LE))) + goto op_next; + ret = compare_guid_key(opinfo, conn->ClientGUID, + lease_key); + if (ret) { + ksmbd_debug(OPLOCK, "found opinfo\n"); + ret_op = opinfo; + goto out; + } +op_next: + opinfo_put(opinfo); + rcu_read_lock(); + } + rcu_read_unlock(); + +out: + read_unlock(&lease_list_lock); + return ret_op; +} diff --git a/fs/ksmbd/oplock.h b/fs/ksmbd/oplock.h new file mode 100644 index 0000000000000..68cdaf405e2d8 --- /dev/null +++ b/fs/ksmbd/oplock.h @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_OPLOCK_H +#define __KSMBD_OPLOCK_H + +#include "smb_common.h" + +#define OPLOCK_WAIT_TIME (35 * HZ) + +#ifdef CONFIG_SMB_INSECURE_SERVER +/* SMB Oplock levels */ +#define OPLOCK_NONE 0 +#define OPLOCK_EXCLUSIVE 1 +#define OPLOCK_BATCH 2 +#define OPLOCK_READ 3 /* level 2 oplock */ +#endif + +/* SMB2 Oplock levels */ +#define SMB2_OPLOCK_LEVEL_NONE 0x00 +#define SMB2_OPLOCK_LEVEL_II 0x01 +#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08 +#define SMB2_OPLOCK_LEVEL_BATCH 0x09 +#define SMB2_OPLOCK_LEVEL_LEASE 0xFF + +/* Oplock states */ +#define OPLOCK_STATE_NONE 0x00 +#define OPLOCK_ACK_WAIT 0x01 +#define OPLOCK_CLOSING 0x02 + +#define OPLOCK_WRITE_TO_READ 0x01 +#define OPLOCK_READ_HANDLE_TO_READ 0x02 +#define OPLOCK_WRITE_TO_NONE 0x04 +#define OPLOCK_READ_TO_NONE 0x08 + +struct lease_ctx_info { + __u8 lease_key[SMB2_LEASE_KEY_SIZE]; + __le32 req_state; + __le32 flags; + __le64 duration; + __u8 parent_lease_key[SMB2_LEASE_KEY_SIZE]; + int version; +}; + +struct lease_table { + char client_guid[SMB2_CLIENT_GUID_SIZE]; + struct list_head lease_list; + struct list_head l_entry; + spinlock_t lb_lock; +}; + +struct lease { + __u8 lease_key[SMB2_LEASE_KEY_SIZE]; + __le32 state; + __le32 new_state; + __le32 flags; + __le64 duration; + __u8 parent_lease_key[SMB2_LEASE_KEY_SIZE]; + int version; + unsigned short epoch; + struct lease_table *l_lb; +}; + +struct oplock_info { + struct ksmbd_conn *conn; + struct ksmbd_session *sess; + struct ksmbd_work *work; + struct ksmbd_file *o_fp; + int level; + int op_state; + unsigned long pending_break; + u64 fid; + atomic_t breaking_cnt; + atomic_t refcount; + __u16 Tid; + bool is_lease; +#ifdef CONFIG_SMB_INSECURE_SERVER + bool is_smb2; +#endif + bool open_trunc; /* truncate on open */ + struct lease *o_lease; + struct list_head interim_list; + struct list_head op_entry; + struct list_head lease_entry; + wait_queue_head_t oplock_q; /* Other server threads */ + wait_queue_head_t oplock_brk; /* oplock breaking wait */ + struct rcu_head rcu_head; +}; + +struct lease_break_info { + __le32 curr_state; + __le32 new_state; + __le16 epoch; + char lease_key[SMB2_LEASE_KEY_SIZE]; +}; + +struct oplock_break_info { + int level; + int open_trunc; + int fid; +}; + +int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, + u64 pid, struct ksmbd_file *fp, __u16 tid, + struct lease_ctx_info *lctx, int share_ret); +void smb_break_all_levII_oplock(struct ksmbd_work *work, + struct ksmbd_file *fp, int is_trunc); +int opinfo_write_to_read(struct oplock_info *opinfo); +int opinfo_read_handle_to_read(struct oplock_info *opinfo); +int opinfo_write_to_none(struct oplock_info *opinfo); +int opinfo_read_to_none(struct oplock_info *opinfo); +void close_id_del_oplock(struct ksmbd_file *fp); +void smb_break_all_oplock(struct ksmbd_work *work, struct ksmbd_file *fp); +struct oplock_info *opinfo_get(struct ksmbd_file *fp); +void opinfo_put(struct oplock_info *opinfo); + +/* Lease related functions */ +void create_lease_buf(u8 *rbuf, struct lease *lease); +struct lease_ctx_info *parse_lease_state(void *open_req); +__u8 smb2_map_lease_to_oplock(__le32 lease_state); +int lease_read_to_write(struct oplock_info *opinfo); + +/* Durable related functions */ +void create_durable_rsp_buf(char *cc); +void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp); +void create_mxac_rsp_buf(char *cc, int maximal_access); +void create_disk_id_rsp_buf(char *cc, __u64 file_id, __u64 vol_id); +void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp); +struct create_context *smb2_find_context_vals(void *open_req, const char *str); +struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, + char *lease_key); +int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, + struct lease_ctx_info *lctx); +void destroy_lease_table(struct ksmbd_conn *conn); +#endif /* __KSMBD_OPLOCK_H */ diff --git a/fs/ksmbd/server.c b/fs/ksmbd/server.c new file mode 100644 index 0000000000000..faab095070074 --- /dev/null +++ b/fs/ksmbd/server.c @@ -0,0 +1,630 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include "glob.h" +#include "oplock.h" +#include "misc.h" +#include +#include +#include +#include +#include + +#include "server.h" +#include "smb_common.h" +#include "smbstatus.h" +#include "connection.h" +#include "transport_ipc.h" +#include "mgmt/user_session.h" +#include "crypto_ctx.h" +#include "auth.h" + +int ksmbd_debug_types; + +struct ksmbd_server_config server_conf; + +enum SERVER_CTRL_TYPE { + SERVER_CTRL_TYPE_INIT, + SERVER_CTRL_TYPE_RESET, +}; + +struct server_ctrl_struct { + int type; + struct work_struct ctrl_work; +}; + +static DEFINE_MUTEX(ctrl_lock); + +static int ___server_conf_set(int idx, char *val) +{ + if (idx >= ARRAY_SIZE(server_conf.conf)) + return -EINVAL; + + if (!val || val[0] == 0x00) + return -EINVAL; + + kfree(server_conf.conf[idx]); + server_conf.conf[idx] = kstrdup(val, GFP_KERNEL); + if (!server_conf.conf[idx]) + return -ENOMEM; + return 0; +} + +int ksmbd_set_netbios_name(char *v) +{ + return ___server_conf_set(SERVER_CONF_NETBIOS_NAME, v); +} + +int ksmbd_set_server_string(char *v) +{ + return ___server_conf_set(SERVER_CONF_SERVER_STRING, v); +} + +int ksmbd_set_work_group(char *v) +{ + return ___server_conf_set(SERVER_CONF_WORK_GROUP, v); +} + +char *ksmbd_netbios_name(void) +{ + return server_conf.conf[SERVER_CONF_NETBIOS_NAME]; +} + +char *ksmbd_server_string(void) +{ + return server_conf.conf[SERVER_CONF_SERVER_STRING]; +} + +char *ksmbd_work_group(void) +{ + return server_conf.conf[SERVER_CONF_WORK_GROUP]; +} + +/** + * check_conn_state() - check state of server thread connection + * @work: smb work containing server thread information + * + * Return: 0 on valid connection, otherwise 1 to reconnect + */ +static inline int check_conn_state(struct ksmbd_work *work) +{ + struct smb_hdr *rsp_hdr; + + if (ksmbd_conn_exiting(work) || ksmbd_conn_need_reconnect(work)) { + rsp_hdr = work->response_buf; + rsp_hdr->Status.CifsError = STATUS_CONNECTION_DISCONNECTED; + return 1; + } + return 0; +} + +#define SERVER_HANDLER_CONTINUE 0 +#define SERVER_HANDLER_ABORT 1 + +static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn, + u16 *cmd) +{ + struct smb_version_cmds *cmds; + u16 command; + int ret; + + if (check_conn_state(work)) + return SERVER_HANDLER_CONTINUE; + + if (ksmbd_verify_smb_message(work)) + return SERVER_HANDLER_ABORT; + + command = conn->ops->get_cmd_val(work); + *cmd = command; + +andx_again: + if (command >= conn->max_cmds) { + conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); + return SERVER_HANDLER_CONTINUE; + } + + cmds = &conn->cmds[command]; + if (!cmds->proc) { + ksmbd_debug(SMB, "*** not implemented yet cmd = %x\n", command); + conn->ops->set_rsp_status(work, STATUS_NOT_IMPLEMENTED); + return SERVER_HANDLER_CONTINUE; + } + + if (work->sess && conn->ops->is_sign_req(work, command)) { + ret = conn->ops->check_sign_req(work); + if (!ret) { + conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED); + return SERVER_HANDLER_CONTINUE; + } + } + + ret = cmds->proc(work); + + if (ret < 0) + ksmbd_debug(CONN, "Failed to process %u [%d]\n", command, ret); + /* AndX commands - chained request can return positive values */ + else if (ret > 0) { + command = ret; + *cmd = command; + goto andx_again; + } + + if (work->send_no_response) + return SERVER_HANDLER_ABORT; + return SERVER_HANDLER_CONTINUE; +} + +static void __handle_ksmbd_work(struct ksmbd_work *work, + struct ksmbd_conn *conn) +{ + u16 command = 0; + int rc; + + if (conn->ops->allocate_rsp_buf(work)) + return; + + if (conn->ops->is_transform_hdr && + conn->ops->is_transform_hdr(work->request_buf)) { + rc = conn->ops->decrypt_req(work); + if (rc < 0) { + conn->ops->set_rsp_status(work, STATUS_DATA_ERROR); + goto send; + } + + work->encrypted = true; + } + + rc = conn->ops->init_rsp_hdr(work); + if (rc) { + /* either uid or tid is not correct */ + conn->ops->set_rsp_status(work, STATUS_INVALID_HANDLE); + goto send; + } + + if (conn->ops->check_user_session) { + rc = conn->ops->check_user_session(work); + if (rc < 0) { + command = conn->ops->get_cmd_val(work); + conn->ops->set_rsp_status(work, + STATUS_USER_SESSION_DELETED); + goto send; + } else if (rc > 0) { + rc = conn->ops->get_ksmbd_tcon(work); + if (rc < 0) { + conn->ops->set_rsp_status(work, + STATUS_NETWORK_NAME_DELETED); + goto send; + } + } + } + + do { + rc = __process_request(work, conn, &command); + if (rc == SERVER_HANDLER_ABORT) + break; + + /* + * Call smb2_set_rsp_credits() function to set number of credits + * granted in hdr of smb2 response. + */ + if (conn->ops->set_rsp_credits) { + spin_lock(&conn->credits_lock); + rc = conn->ops->set_rsp_credits(work); + spin_unlock(&conn->credits_lock); + if (rc < 0) { + conn->ops->set_rsp_status(work, + STATUS_INVALID_PARAMETER); + goto send; + } + } + + if (work->sess && + (work->sess->sign || smb3_11_final_sess_setup_resp(work) || + conn->ops->is_sign_req(work, command))) + conn->ops->set_sign_rsp(work); + } while (is_chained_smb2_message(work)); + + if (work->send_no_response) + return; + +send: + smb3_preauth_hash_rsp(work); + if (work->sess && work->sess->enc && work->encrypted && + conn->ops->encrypt_resp) { + rc = conn->ops->encrypt_resp(work); + if (rc < 0) + conn->ops->set_rsp_status(work, STATUS_DATA_ERROR); + } + + ksmbd_conn_write(work); +} + +/** + * handle_ksmbd_work() - process pending smb work requests + * @wk: smb work containing request command buffer + * + * called by kworker threads to processing remaining smb work requests + */ +static void handle_ksmbd_work(struct work_struct *wk) +{ + struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); + struct ksmbd_conn *conn = work->conn; + + atomic64_inc(&conn->stats.request_served); + + __handle_ksmbd_work(work, conn); + + ksmbd_conn_try_dequeue_request(work); + ksmbd_free_work_struct(work); + /* + * Checking waitqueue to dropping pending requests on + * disconnection. waitqueue_active is safe because it + * uses atomic operation for condition. + */ + if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q)) + wake_up(&conn->r_count_q); +} + +/** + * queue_ksmbd_work() - queue a smb request to worker thread queue + * for proccessing smb command and sending response + * @conn: connection instance + * + * read remaining data from socket create and submit work. + */ +static int queue_ksmbd_work(struct ksmbd_conn *conn) +{ + struct ksmbd_work *work; + + work = ksmbd_alloc_work_struct(); + if (!work) { + pr_err("allocation for work failed\n"); + return -ENOMEM; + } + + work->conn = conn; + work->request_buf = conn->request_buf; + conn->request_buf = NULL; + + if (ksmbd_init_smb_server(work)) { + ksmbd_free_work_struct(work); + return -EINVAL; + } + + ksmbd_conn_enqueue_request(work); + atomic_inc(&conn->r_count); + /* update activity on connection */ + conn->last_active = jiffies; + INIT_WORK(&work->work, handle_ksmbd_work); + ksmbd_queue_work(work); + return 0; +} + +static int ksmbd_server_process_request(struct ksmbd_conn *conn) +{ + return queue_ksmbd_work(conn); +} + +static int ksmbd_server_terminate_conn(struct ksmbd_conn *conn) +{ + ksmbd_sessions_deregister(conn); + destroy_lease_table(conn); + return 0; +} + +static void ksmbd_server_tcp_callbacks_init(void) +{ + struct ksmbd_conn_ops ops; + + ops.process_fn = ksmbd_server_process_request; + ops.terminate_fn = ksmbd_server_terminate_conn; + + ksmbd_conn_init_server_callbacks(&ops); +} + +static void server_conf_free(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(server_conf.conf); i++) { + kfree(server_conf.conf[i]); + server_conf.conf[i] = NULL; + } +} + +static int server_conf_init(void) +{ + WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP); + server_conf.enforced_signing = 0; + server_conf.min_protocol = ksmbd_min_protocol(); + server_conf.max_protocol = ksmbd_max_protocol(); + server_conf.auth_mechs = KSMBD_AUTH_NTLMSSP; +#ifdef CONFIG_SMB_SERVER_KERBEROS5 + server_conf.auth_mechs |= KSMBD_AUTH_KRB5 | + KSMBD_AUTH_MSKRB5; +#endif + return 0; +} + +static void server_ctrl_handle_init(struct server_ctrl_struct *ctrl) +{ + int ret; + + ret = ksmbd_conn_transport_init(); + if (ret) { + server_queue_ctrl_reset_work(); + return; + } + + WRITE_ONCE(server_conf.state, SERVER_STATE_RUNNING); +} + +static void server_ctrl_handle_reset(struct server_ctrl_struct *ctrl) +{ + ksmbd_ipc_soft_reset(); + ksmbd_conn_transport_destroy(); + server_conf_free(); + server_conf_init(); + WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP); +} + +static void server_ctrl_handle_work(struct work_struct *work) +{ + struct server_ctrl_struct *ctrl; + + ctrl = container_of(work, struct server_ctrl_struct, ctrl_work); + + mutex_lock(&ctrl_lock); + switch (ctrl->type) { + case SERVER_CTRL_TYPE_INIT: + server_ctrl_handle_init(ctrl); + break; + case SERVER_CTRL_TYPE_RESET: + server_ctrl_handle_reset(ctrl); + break; + default: + pr_err("Unknown server work type: %d\n", ctrl->type); + } + mutex_unlock(&ctrl_lock); + kfree(ctrl); + module_put(THIS_MODULE); +} + +static int __queue_ctrl_work(int type) +{ + struct server_ctrl_struct *ctrl; + + ctrl = kmalloc(sizeof(struct server_ctrl_struct), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + __module_get(THIS_MODULE); + ctrl->type = type; + INIT_WORK(&ctrl->ctrl_work, server_ctrl_handle_work); + queue_work(system_long_wq, &ctrl->ctrl_work); + return 0; +} + +int server_queue_ctrl_init_work(void) +{ + return __queue_ctrl_work(SERVER_CTRL_TYPE_INIT); +} + +int server_queue_ctrl_reset_work(void) +{ + return __queue_ctrl_work(SERVER_CTRL_TYPE_RESET); +} + +static ssize_t stats_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + /* + * Inc this each time you change stats output format, + * so user space will know what to do. + */ + static int stats_version = 2; + static const char * const state[] = { + "startup", + "running", + "reset", + "shutdown" + }; + return sysfs_emit(buf, "%d %s %d %lu\n", stats_version, + state[server_conf.state], server_conf.tcp_port, + server_conf.ipc_last_active / HZ); +} + +static ssize_t kill_server_store(struct class *class, + struct class_attribute *attr, const char *buf, + size_t len) +{ + if (!sysfs_streq(buf, "hard")) + return len; + + pr_info("kill command received\n"); + mutex_lock(&ctrl_lock); + WRITE_ONCE(server_conf.state, SERVER_STATE_RESETTING); + __module_get(THIS_MODULE); + server_ctrl_handle_reset(NULL); + module_put(THIS_MODULE); + mutex_unlock(&ctrl_lock); + return len; +} + +static const char * const debug_type_strings[] = {"smb", "auth", "vfs", + "oplock", "ipc", "conn", + "rdma"}; + +static ssize_t debug_show(struct class *class, struct class_attribute *attr, + char *buf) +{ + ssize_t sz = 0; + int i, pos = 0; + + for (i = 0; i < ARRAY_SIZE(debug_type_strings); i++) { + if ((ksmbd_debug_types >> i) & 1) { + pos = sysfs_emit_at(buf, sz, "[%s] ", debug_type_strings[i]); + } else { + pos = sysfs_emit_at(buf, sz, "%s ", debug_type_strings[i]); + } + sz += pos; + } + sz += sysfs_emit_at(buf, sz, "\n"); + return sz; +} + +static ssize_t debug_store(struct class *class, struct class_attribute *attr, + const char *buf, size_t len) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(debug_type_strings); i++) { + if (sysfs_streq(buf, "all")) { + if (ksmbd_debug_types == KSMBD_DEBUG_ALL) + ksmbd_debug_types = 0; + else + ksmbd_debug_types = KSMBD_DEBUG_ALL; + break; + } + + if (sysfs_streq(buf, debug_type_strings[i])) { + if (ksmbd_debug_types & (1 << i)) + ksmbd_debug_types &= ~(1 << i); + else + ksmbd_debug_types |= (1 << i); + break; + } + } + + return len; +} + +static CLASS_ATTR_RO(stats); +static CLASS_ATTR_WO(kill_server); +static CLASS_ATTR_RW(debug); + +static struct attribute *ksmbd_control_class_attrs[] = { + &class_attr_stats.attr, + &class_attr_kill_server.attr, + &class_attr_debug.attr, + NULL, +}; +ATTRIBUTE_GROUPS(ksmbd_control_class); + +static struct class ksmbd_control_class = { + .name = "ksmbd-control", + .owner = THIS_MODULE, + .class_groups = ksmbd_control_class_groups, +}; + +static int ksmbd_server_shutdown(void) +{ + WRITE_ONCE(server_conf.state, SERVER_STATE_SHUTTING_DOWN); + + class_unregister(&ksmbd_control_class); + ksmbd_workqueue_destroy(); + ksmbd_ipc_release(); + ksmbd_conn_transport_destroy(); + ksmbd_crypto_destroy(); + ksmbd_free_global_file_table(); + destroy_lease_table(NULL); + ksmbd_work_pool_destroy(); + ksmbd_exit_file_cache(); + server_conf_free(); + return 0; +} + +static int __init ksmbd_server_init(void) +{ + int ret; + + ret = class_register(&ksmbd_control_class); + if (ret) { + pr_err("Unable to register ksmbd-control class\n"); + return ret; + } + + ksmbd_server_tcp_callbacks_init(); + + ret = server_conf_init(); + if (ret) + goto err_unregister; + + ret = ksmbd_work_pool_init(); + if (ret) + goto err_unregister; + + ret = ksmbd_init_file_cache(); + if (ret) + goto err_destroy_work_pools; + + ret = ksmbd_ipc_init(); + if (ret) + goto err_exit_file_cache; + + ret = ksmbd_init_global_file_table(); + if (ret) + goto err_ipc_release; + + ret = ksmbd_inode_hash_init(); + if (ret) + goto err_destroy_file_table; + + ret = ksmbd_crypto_create(); + if (ret) + goto err_release_inode_hash; + + ret = ksmbd_workqueue_init(); + if (ret) + goto err_crypto_destroy; + return 0; + +err_crypto_destroy: + ksmbd_crypto_destroy(); +err_release_inode_hash: + ksmbd_release_inode_hash(); +err_destroy_file_table: + ksmbd_free_global_file_table(); +err_ipc_release: + ksmbd_ipc_release(); +err_exit_file_cache: + ksmbd_exit_file_cache(); +err_destroy_work_pools: + ksmbd_work_pool_destroy(); +err_unregister: + class_unregister(&ksmbd_control_class); + + return ret; +} + +/** + * ksmbd_server_exit() - shutdown forker thread and free memory at module exit + */ +static void __exit ksmbd_server_exit(void) +{ + ksmbd_server_shutdown(); + ksmbd_release_inode_hash(); +} + +MODULE_AUTHOR("Namjae Jeon "); +MODULE_VERSION(KSMBD_VERSION); +MODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER"); +MODULE_LICENSE("GPL"); +MODULE_SOFTDEP("pre: ecb"); +MODULE_SOFTDEP("pre: hmac"); +MODULE_SOFTDEP("pre: md4"); +MODULE_SOFTDEP("pre: md5"); +MODULE_SOFTDEP("pre: nls"); +MODULE_SOFTDEP("pre: aes"); +MODULE_SOFTDEP("pre: cmac"); +MODULE_SOFTDEP("pre: sha256"); +MODULE_SOFTDEP("pre: sha512"); +MODULE_SOFTDEP("pre: aead2"); +MODULE_SOFTDEP("pre: ccm"); +MODULE_SOFTDEP("pre: gcm"); +MODULE_SOFTDEP("pre: crc32"); +module_init(ksmbd_server_init) +module_exit(ksmbd_server_exit) diff --git a/fs/ksmbd/server.h b/fs/ksmbd/server.h new file mode 100644 index 0000000000000..db72781817603 --- /dev/null +++ b/fs/ksmbd/server.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __SERVER_H__ +#define __SERVER_H__ + +#include "smbacl.h" + +/* + * Server state type + */ +enum { + SERVER_STATE_STARTING_UP, + SERVER_STATE_RUNNING, + SERVER_STATE_RESETTING, + SERVER_STATE_SHUTTING_DOWN, +}; + +/* + * Server global config string index + */ +enum { + SERVER_CONF_NETBIOS_NAME, + SERVER_CONF_SERVER_STRING, + SERVER_CONF_WORK_GROUP, +}; + +struct ksmbd_server_config { + unsigned int flags; + unsigned int state; + short signing; + short enforced_signing; + short min_protocol; + short max_protocol; + unsigned short tcp_port; + unsigned short ipc_timeout; + unsigned long ipc_last_active; + unsigned long deadtime; + unsigned int share_fake_fscaps; + struct smb_sid domain_sid; + unsigned int auth_mechs; + unsigned int max_connections; + + char *conf[SERVER_CONF_WORK_GROUP + 1]; +}; + +extern struct ksmbd_server_config server_conf; + +int ksmbd_set_netbios_name(char *v); +int ksmbd_set_server_string(char *v); +int ksmbd_set_work_group(char *v); + +char *ksmbd_netbios_name(void); +char *ksmbd_server_string(void); +char *ksmbd_work_group(void); + +static inline int ksmbd_server_running(void) +{ + return READ_ONCE(server_conf.state) == SERVER_STATE_RUNNING; +} + +static inline int ksmbd_server_configurable(void) +{ + return READ_ONCE(server_conf.state) < SERVER_STATE_RESETTING; +} + +int server_queue_ctrl_init_work(void); +int server_queue_ctrl_reset_work(void); +#endif /* __SERVER_H__ */ diff --git a/fs/ksmbd/smb1misc.c b/fs/ksmbd/smb1misc.c new file mode 100644 index 0000000000000..e29581d9280e7 --- /dev/null +++ b/fs/ksmbd/smb1misc.c @@ -0,0 +1,297 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include "glob.h" +#include "asn1.h" +#include "nterr.h" +#include "ksmbd_work.h" +#include "smb_common.h" +#include "smb1pdu.h" +#include "mgmt/user_session.h" + +/** + * check_smb_hdr() - check for valid smb request header + * @smb: smb header to be checked + * + * check for valid smb signature and packet direction(request/response) + * TODO: properly check client authetication and tree authentication + * + * Return: 0 on success, otherwise 1 + */ +static int check_smb1_hdr(struct smb_hdr *smb) +{ + /* does it have the right SMB "signature" ? */ + if (*(__le32 *) smb->Protocol != SMB1_PROTO_NUMBER) { + ksmbd_debug(SMB, "Bad protocol string signature header 0x%x\n", + *(unsigned int *)smb->Protocol); + return 1; + } + ksmbd_debug(SMB, "got SMB\n"); + + /* if it's not a response then accept */ + /* TODO : check for oplock break */ + if (!(smb->Flags & SMBFLG_RESPONSE)) + return 0; + + ksmbd_debug(SMB, "Server sent request, not response\n"); + return 1; +} + + +static int smb1_req_struct_size(struct smb_hdr *hdr) +{ + int wc = hdr->WordCount; + + switch (hdr->Command) { + case SMB_COM_CREATE_DIRECTORY: + case SMB_COM_DELETE_DIRECTORY: + case SMB_COM_QUERY_INFORMATION: + case SMB_COM_TREE_DISCONNECT: + case SMB_COM_NEGOTIATE: + case SMB_COM_NT_CANCEL: + case SMB_COM_CHECK_DIRECTORY: + case SMB_COM_PROCESS_EXIT: + if (wc != 0x0) + return -EINVAL; + break; + case SMB_COM_FLUSH: + case SMB_COM_DELETE: + case SMB_COM_RENAME: + case SMB_COM_ECHO: + case SMB_COM_FIND_CLOSE2: + if (wc != 0x1) + return -EINVAL; + break; + case SMB_COM_LOGOFF_ANDX: + if (wc != 0x2) + return -EINVAL; + break; + case SMB_COM_CLOSE: + if (wc != 0x3) + return -EINVAL; + break; + case SMB_COM_TREE_CONNECT_ANDX: + case SMB_COM_NT_RENAME: + if (wc != 0x4) + return -EINVAL; + break; + case SMB_COM_WRITE: + if (wc != 0x5) + return -EINVAL; + break; + case SMB_COM_SETATTR: + case SMB_COM_LOCKING_ANDX: + if (wc != 0x8) + return -EINVAL; + break; + case SMB_COM_TRANSACTION: + if (wc < 0xe) + return -EINVAL; + break; + case SMB_COM_SESSION_SETUP_ANDX: + if (wc != 0xc && wc != 0xd) + return -EINVAL; + break; + case SMB_COM_OPEN_ANDX: + case SMB_COM_TRANSACTION2: + if (wc != 0xf) + return -EINVAL; + break; + case SMB_COM_NT_CREATE_ANDX: + if (wc != 0x18) + return -EINVAL; + break; + case SMB_COM_READ_ANDX: + if (wc != 0xa && wc != 0xc) + return -EINVAL; + break; + case SMB_COM_WRITE_ANDX: + if (wc != 0xc && wc != 0xe) + return -EINVAL; + break; + default: + return -EOPNOTSUPP; + } + + return wc; +} + +static int smb1_get_byte_count(struct smb_hdr *hdr) +{ + int bc; + + bc = le16_to_cpu(*(__le16 *)((char *)hdr + + sizeof(struct smb_hdr) + hdr->WordCount * 2)); + + switch (hdr->Command) { + case SMB_COM_CLOSE: + case SMB_COM_FLUSH: + case SMB_COM_READ_ANDX: + case SMB_COM_TREE_DISCONNECT: + case SMB_COM_LOGOFF_ANDX: + case SMB_COM_NT_CANCEL: + case SMB_COM_PROCESS_EXIT: + case SMB_COM_FIND_CLOSE2: + if (bc != 0x0) + return -EINVAL; + break; + case SMB_COM_LOCKING_ANDX: + case SMB_COM_TRANSACTION: + case SMB_COM_TRANSACTION2: + case SMB_COM_ECHO: + case SMB_COM_SESSION_SETUP_ANDX: + if (bc < 0x0) + return -EINVAL; + break; + case SMB_COM_WRITE_ANDX: + if (bc < 0x1) + return -EINVAL; + break; + case SMB_COM_CREATE_DIRECTORY: + case SMB_COM_DELETE_DIRECTORY: + case SMB_COM_DELETE: + case SMB_COM_RENAME: + case SMB_COM_QUERY_INFORMATION: + case SMB_COM_SETATTR: + case SMB_COM_OPEN_ANDX: + case SMB_COM_NEGOTIATE: + case SMB_COM_CHECK_DIRECTORY: + if (bc < 0x2) + return -EINVAL; + break; + case SMB_COM_TREE_CONNECT_ANDX: + case SMB_COM_WRITE: + if (bc < 0x3) + return -EINVAL; + break; + case SMB_COM_NT_RENAME: + if (bc < 0x4) + return -EINVAL; + break; + case SMB_COM_NT_CREATE_ANDX: + if (hdr->Flags2 & SMBFLG2_UNICODE) { + if (bc < 3) + return -EINVAL; + } else if (bc < 2) + return -EINVAL; + break; + } + + return bc; +} + +static unsigned int smb1_calc_size(struct smb_hdr *hdr) +{ + int len = sizeof(struct smb_hdr) - 4 + 2; + int bc, struct_size = hdr->WordCount * 2; + + len += struct_size; + bc = smb1_get_byte_count(hdr); + if (bc < 0) + return bc; + ksmbd_debug(SMB, "SMB2 byte count %d, struct size : %d\n", bc, + struct_size); + len += bc; + + ksmbd_debug(SMB, "SMB1 len %d\n", len); + return len; +} + +static int smb1_get_data_len(struct smb_hdr *hdr) +{ + int data_len = 0; + + /* data offset check */ + switch (hdr->Command) { + case SMB_COM_WRITE_ANDX: + { + struct smb_com_write_req *req = (struct smb_com_write_req *)hdr; + + data_len = le16_to_cpu(req->DataLengthLow); + data_len |= (le16_to_cpu(req->DataLengthHigh) << 16); + data_len += le16_to_cpu(req->DataOffset); + break; + } + case SMB_COM_TRANSACTION: + { + struct smb_com_trans_req *req = (struct smb_com_trans_req *)hdr; + + data_len = le16_to_cpu(req->DataOffset) + + le16_to_cpu(req->DataCount); + break; + } + case SMB_COM_TRANSACTION2: + { + struct smb_com_trans2_req *req = + (struct smb_com_trans2_req *)hdr; + + data_len = le16_to_cpu(req->DataOffset) + + le16_to_cpu(req->DataCount); + break; + } + } + + return data_len; +} + +int ksmbd_smb1_check_message(struct ksmbd_work *work) +{ + struct smb_hdr *hdr = (struct smb_hdr *)work->request_buf; + char *buf = work->request_buf; + int command = hdr->Command; + __u32 clc_len; /* calculated length */ + __u32 len = get_rfc1002_len(buf); + int wc, data_len; + + if (check_smb1_hdr(hdr)) + return 1; + + wc = smb1_req_struct_size(hdr); + if (wc == -EOPNOTSUPP) { + ksmbd_debug(SMB, "Not support cmd %x\n", command); + return 1; + } else if (hdr->WordCount != wc) { + pr_err("Invalid word count, %d not %d. cmd %x\n", + hdr->WordCount, wc, command); + return 1; + } + + data_len = smb1_get_data_len(hdr); + if (len < data_len) { + pr_err("Invalid data area length %u not %u. cmd : %x\n", + len, data_len, command); + return 1; + } + + clc_len = smb1_calc_size(hdr); + if (len != clc_len) { + /* + * smbclient may return wrong byte count in smb header. + * But allow it to avoid write failure with smbclient. + */ + if (command == SMB_COM_WRITE_ANDX) + return 0; + + if (len > clc_len) { + ksmbd_debug(SMB, + "cli req too long, len %d not %d. cmd:%x\n", + len, clc_len, command); + return 0; + } + + pr_err("cli req too short, len %d not %d. cmd:%x\n", + len, clc_len, command); + + return 1; + } + + return 0; +} + +int smb_negotiate_request(struct ksmbd_work *work) +{ + return ksmbd_smb_negotiate_common(work, SMB_COM_NEGOTIATE); +} diff --git a/fs/ksmbd/smb1ops.c b/fs/ksmbd/smb1ops.c new file mode 100644 index 0000000000000..b1e2aac1c1406 --- /dev/null +++ b/fs/ksmbd/smb1ops.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include + +#include "glob.h" +#include "connection.h" +#include "smb_common.h" +#include "smb1pdu.h" + +static struct smb_version_values smb1_server_values = { + .version_string = SMB1_VERSION_STRING, + .protocol_id = SMB10_PROT_ID, + .capabilities = SMB1_SERVER_CAPS, + .max_read_size = CIFS_DEFAULT_IOSIZE, + .max_write_size = CIFS_DEFAULT_IOSIZE, + .max_trans_size = CIFS_DEFAULT_IOSIZE, + .large_lock_type = LOCKING_ANDX_LARGE_FILES, + .exclusive_lock_type = 0, + .shared_lock_type = LOCKING_ANDX_SHARED_LOCK, + .unlock_lock_type = 0, + .header_size = sizeof(struct smb_hdr), + .max_header_size = MAX_CIFS_HDR_SIZE, + .read_rsp_size = sizeof(struct smb_com_read_rsp), + .lock_cmd = cpu_to_le16(SMB_COM_LOCKING_ANDX), + .cap_unix = CAP_UNIX, + .cap_nt_find = CAP_NT_SMBS | CAP_NT_FIND, + .cap_large_files = CAP_LARGE_FILES, + .signing_enabled = SECMODE_SIGN_ENABLED, + .signing_required = SECMODE_SIGN_REQUIRED, +}; + +static struct smb_version_ops smb1_server_ops = { + .get_cmd_val = get_smb_cmd_val, + .init_rsp_hdr = init_smb_rsp_hdr, + .set_rsp_status = set_smb_rsp_status, + .allocate_rsp_buf = smb_allocate_rsp_buf, + .check_user_session = smb_check_user_session, + .is_sign_req = smb1_is_sign_req, + .check_sign_req = smb1_check_sign_req, + .set_sign_rsp = smb1_set_sign_rsp, + .get_ksmbd_tcon = smb_get_ksmbd_tcon, +}; + +static struct smb_version_cmds smb1_server_cmds[256] = { + [SMB_COM_CREATE_DIRECTORY] = { .proc = smb_mkdir, }, + [SMB_COM_DELETE_DIRECTORY] = { .proc = smb_rmdir, }, + [SMB_COM_CLOSE] = { .proc = smb_close, }, + [SMB_COM_FLUSH] = { .proc = smb_flush, }, + [SMB_COM_DELETE] = { .proc = smb_unlink, }, + [SMB_COM_RENAME] = { .proc = smb_rename, }, + [SMB_COM_QUERY_INFORMATION] = { .proc = smb_query_info, }, + [SMB_COM_SETATTR] = { .proc = smb_setattr, }, + [SMB_COM_LOCKING_ANDX] = { .proc = smb_locking_andx, }, + [SMB_COM_TRANSACTION] = { .proc = smb_trans, }, + [SMB_COM_ECHO] = { .proc = smb_echo, }, + [SMB_COM_OPEN_ANDX] = { .proc = smb_open_andx, }, + [SMB_COM_READ_ANDX] = { .proc = smb_read_andx, }, + [SMB_COM_WRITE_ANDX] = { .proc = smb_write_andx, }, + [SMB_COM_TRANSACTION2] = { .proc = smb_trans2, }, + [SMB_COM_FIND_CLOSE2] = { .proc = smb_closedir, }, + [SMB_COM_TREE_DISCONNECT] = { .proc = smb_tree_disconnect, }, + [SMB_COM_NEGOTIATE] = { .proc = smb_negotiate_request, }, + [SMB_COM_SESSION_SETUP_ANDX] = { .proc = smb_session_setup_andx, }, + [SMB_COM_LOGOFF_ANDX] = { .proc = smb_session_disconnect, }, + [SMB_COM_TREE_CONNECT_ANDX] = { .proc = smb_tree_connect_andx, }, + [SMB_COM_NT_CREATE_ANDX] = { .proc = smb_nt_create_andx, }, + [SMB_COM_NT_CANCEL] = { .proc = smb_nt_cancel, }, + [SMB_COM_NT_RENAME] = { .proc = smb_nt_rename, }, + [SMB_COM_WRITE] = { .proc = smb_write, }, + [SMB_COM_CHECK_DIRECTORY] = { .proc = smb_checkdir, }, + [SMB_COM_PROCESS_EXIT] = { .proc = smb_process_exit, }, +}; + +/** + * init_smb1_server() - initialize a smb server connection with smb1 + * command dispatcher + * @conn: connection instance + */ +int init_smb1_server(struct ksmbd_conn *conn) +{ + if (!conn) + return -EINVAL; + + conn->vals = &smb1_server_values; + conn->ops = &smb1_server_ops; + conn->cmds = smb1_server_cmds; + conn->max_cmds = ARRAY_SIZE(smb1_server_cmds); + return 0; +} diff --git a/fs/ksmbd/smb1pdu.c b/fs/ksmbd/smb1pdu.c new file mode 100644 index 0000000000000..ea12033042263 --- /dev/null +++ b/fs/ksmbd/smb1pdu.c @@ -0,0 +1,8511 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ +#include +#include +#include +#include +#include +#include + +#include "glob.h" +#include "oplock.h" +#include "connection.h" +#include "transport_ipc.h" +#include "vfs.h" +#include "misc.h" + +#include "auth.h" +#include "asn1.h" +#include "server.h" +#include "smb_common.h" +#include "smb1pdu.h" +#include "smbstatus.h" +#include "mgmt/user_config.h" +#include "mgmt/share_config.h" +#include "mgmt/tree_connect.h" +#include "mgmt/user_session.h" +#include "ndr.h" +#include "smberr.h" + +static int smb1_oplock_enable = false; + +/* Default: allocation roundup size = 1048576 */ +static unsigned int alloc_roundup_size = 1048576; + +struct ksmbd_dirent { + unsigned long long ino; + unsigned long long offset; + unsigned int namelen; + unsigned int d_type; + char name[]; +}; + +/** + * smb_NTtimeToUnix() - convert NTFS time to unix style time format + * @ntutc: NTFS style time + * + * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units) + * into Unix UTC (based 1970-01-01, in seconds). + * + * Return: timespec containing unix style time + */ +static struct timespec64 smb_NTtimeToUnix(__le64 ntutc) +{ + struct timespec64 ts; + + /* BB what about the timezone? BB */ + + /* Subtract the NTFS time offset, then convert to 1s intervals. */ + /* this has been taken from cifs, ntfs code */ + u64 t; + + t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET; + ts.tv_nsec = do_div(t, 10000000) * 100; + ts.tv_sec = t; + return ts; +} + +/** + * get_smb_cmd_val() - get smb command value from smb header + * @work: smb work containing smb header + * + * Return: smb command value + */ +u16 get_smb_cmd_val(struct ksmbd_work *work) +{ + struct smb_hdr *rcv_hdr = (struct smb_hdr *)work->request_buf; + + return (u16)rcv_hdr->Command; +} + +/** + * is_smbreq_unicode() - check if the smb command is request is unicode or not + * @hdr: pointer to smb_hdr in the the request part + * + * Return: check flags and return true if request is unicode, else false + */ +static inline int is_smbreq_unicode(struct smb_hdr *hdr) +{ + return hdr->Flags2 & SMBFLG2_UNICODE ? 1 : 0; +} + +/** + * set_smb_rsp_status() - set error type in smb response header + * @work: smb work containing smb response header + * @err: error code to set in response + */ +void set_smb_rsp_status(struct ksmbd_work *work, __le32 err) +{ + struct smb_hdr *rsp_hdr = (struct smb_hdr *) work->response_buf; + + rsp_hdr->Status.CifsError = err; +} + +/** + * init_smb_rsp_hdr() - initialize smb response header + * @work: smb work containing smb request + * + * Return: 0 on success, otherwise -EINVAL + */ +int init_smb_rsp_hdr(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb_hdr *rsp_hdr; + struct smb_hdr *rcv_hdr = (struct smb_hdr *)work->request_buf; + + rsp_hdr = (struct smb_hdr *) work->response_buf; + memset(rsp_hdr, 0, sizeof(struct smb_hdr) + 2); + + /* remove 4 byte direct TCP header, add 1 byte wc and 2 byte bcc */ + rsp_hdr->smb_buf_length = + cpu_to_be32(conn->vals->header_size - 4 + 2); + memcpy(rsp_hdr->Protocol, rcv_hdr->Protocol, 4); + rsp_hdr->Command = rcv_hdr->Command; + + /* + * Message is response. Other bits are obsolete. + */ + rsp_hdr->Flags = (SMBFLG_RESPONSE); + + /* + * Lets assume error code are NTLM. True for CIFS and windows 7 + */ + rsp_hdr->Flags2 = rcv_hdr->Flags2; + rsp_hdr->PidHigh = rcv_hdr->PidHigh; + rsp_hdr->Pid = rcv_hdr->Pid; + rsp_hdr->Mid = rcv_hdr->Mid; + rsp_hdr->WordCount = 0; + + /* We can do the above test because we have set maxVCN as 1 */ + rsp_hdr->Uid = rcv_hdr->Uid; + rsp_hdr->Tid = rcv_hdr->Tid; + return 0; +} + +/** + * smb_allocate_rsp_buf() - allocate response buffer for a command + * @work: smb work containing smb request + * + * Return: 0 on success, otherwise -ENOMEM + */ +int smb_allocate_rsp_buf(struct ksmbd_work *work) +{ + struct smb_hdr *hdr = (struct smb_hdr *)work->request_buf; + unsigned char cmd = hdr->Command; + size_t large_sz = work->conn->vals->max_read_size + MAX_CIFS_HDR_SIZE; + size_t sz = MAX_CIFS_SMALL_BUFFER_SIZE; + + if (cmd == SMB_COM_TRANSACTION2) { + struct smb_com_trans2_qpi_req *req = work->request_buf; + u16 sub_cmd = le16_to_cpu(req->SubCommand); + u16 infolevel = le16_to_cpu(req->InformationLevel); + + if ((sub_cmd == TRANS2_FIND_FIRST) || + (sub_cmd == TRANS2_FIND_NEXT) || + (sub_cmd == TRANS2_QUERY_PATH_INFORMATION && + (infolevel == SMB_QUERY_FILE_UNIX_LINK || + infolevel == SMB_QUERY_POSIX_ACL || + infolevel == SMB_INFO_QUERY_ALL_EAS))) + sz = large_sz; + } + + if (cmd == SMB_COM_TRANSACTION) + sz = large_sz; + + if (cmd == SMB_COM_ECHO) { + int resp_size; + struct smb_com_echo_req *req = work->request_buf; + + /* + * size of struct smb_com_echo_rsp + Bytecount - Size of Data + * in struct smb_com_echo_rsp + */ + resp_size = sizeof(struct smb_com_echo_rsp) + + le16_to_cpu(req->ByteCount) - 1; + if (resp_size > MAX_CIFS_SMALL_BUFFER_SIZE) + sz = large_sz; + } + + work->response_buf = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO); + work->response_sz = sz; + + if (!work->response_buf) { + pr_err("Failed to allocate %zu bytes buffer\n", sz); + return -ENOMEM; + } + + return 0; +} + +/** + * andx_request_buffer() - return pointer to matching andx command + * @work: buffer containing smb request + * @command: match next command with this command + * + * Return: pointer to matching command buffer on success, otherwise NULL + */ +static char *andx_request_buffer(char *buf, int command) +{ + struct andx_block *andx_ptr = (struct andx_block *)(buf + + sizeof(struct smb_hdr) - 1); + struct andx_block *next; + + while (andx_ptr->AndXCommand != SMB_NO_MORE_ANDX_COMMAND) { + next = (struct andx_block *) + (buf + 4 + le16_to_cpu(andx_ptr->AndXOffset)); + if (andx_ptr->AndXCommand == command) + return (char *)next; + andx_ptr = next; + } + return NULL; +} + +/** + * andx_response_buffer() - return pointer to andx response buffer + * @buf: buffer containing smb request + * + * Return: pointer to andx command response on success, otherwise NULL + */ +static char *andx_response_buffer(char *buf) +{ + int pdu_length = get_rfc1002_len(buf); + + return buf + 4 + pdu_length; +} + +/** + * smb_check_user_session() - check for valid session for a user + * @work: smb work containing smb request buffer + * + * Return: 0 on success, otherwise error + */ +int smb_check_user_session(struct ksmbd_work *work) +{ + struct smb_hdr *req_hdr = (struct smb_hdr *)work->request_buf; + struct ksmbd_conn *conn = work->conn; + unsigned int cmd = conn->ops->get_cmd_val(work); + + work->sess = NULL; + if (cmd == SMB_COM_NEGOTIATE || cmd == SMB_COM_SESSION_SETUP_ANDX || + cmd == SMB_COM_ECHO) + return 0; + + if (!ksmbd_conn_good(work)) + return -EINVAL; + + if (xa_empty(&conn->sessions)) { + ksmbd_debug(SMB, "NO sessions registered\n"); + return -EINVAL; + } + + work->sess = ksmbd_session_lookup(conn, le16_to_cpu(req_hdr->Uid)); + if (work->sess) + return 1; + ksmbd_debug(SMB, "Invalid user session, Uid %u\n", + le16_to_cpu(req_hdr->Uid)); + return -EINVAL; +} + +/** + * smb_get_ksmbd_tcon() - get tree connection information for a tree id + * @sess: session containing tree list + * @tid: match tree connection with tree id + * + * Return: matching tree connection on success, otherwise error + */ +int smb_get_ksmbd_tcon(struct ksmbd_work *work) +{ + struct smb_hdr *req_hdr = (struct smb_hdr *)work->request_buf; + u8 cmd = req_hdr->Command; + int tree_id; + + work->tcon = NULL; + if (cmd == SMB_COM_TREE_CONNECT_ANDX || + cmd == SMB_COM_NT_CANCEL || + cmd == SMB_COM_LOGOFF_ANDX) { + ksmbd_debug(SMB, "skip to check tree connect request\n"); + return 0; + } + + if (xa_empty(&work->sess->tree_conns)) { + ksmbd_debug(SMB, "NO tree connected\n"); + return -ENOENT; + } + + tree_id = le16_to_cpu(req_hdr->Tid); + work->tcon = ksmbd_tree_conn_lookup(work->sess, tree_id); + if (!work->tcon) { + pr_err("Invalid tid %d\n", tree_id); + return -EINVAL; + } + + return 1; +} + +/** + * smb_session_disconnect() - LOGOFF request handler + * @work: smb work containing log off request buffer + * + * Return: 0 on success, otherwise error + */ +int smb_session_disconnect(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + + /* setting CifsExiting here may race with start_tcp_sess */ + ksmbd_conn_set_need_reconnect(work); + + ksmbd_free_user(sess->user); + sess->user = NULL; + + ksmbd_conn_wait_idle(conn); + + ksmbd_tree_conn_session_logoff(sess); + xa_erase(&conn->sessions, sess->id); + ksmbd_session_destroy(sess); + work->sess = NULL; + + /* let start_tcp_sess free conn info now */ + ksmbd_conn_set_exiting(work); + return 0; +} + +/** + * smb_session_disconnect() - tree disconnect request handler + * @work: smb work containing tree disconnect request buffer + * + * Return: 0 on success, otherwise error + */ +int smb_tree_disconnect(struct ksmbd_work *work) +{ + struct smb_hdr *req_hdr = (struct smb_hdr *)work->request_buf; + struct smb_hdr *rsp_hdr = (struct smb_hdr *)work->response_buf; + struct ksmbd_tree_connect *tcon = work->tcon; + struct ksmbd_session *sess = work->sess; + + if (!tcon) { + pr_err("Invalid tid %d\n", req_hdr->Tid); + rsp_hdr->Status.CifsError = STATUS_NO_SUCH_USER; + return -EINVAL; + } + + ksmbd_close_tree_conn_fds(work); + ksmbd_tree_conn_disconnect(sess, tcon); + return 0; +} + +static void set_service_type(struct ksmbd_conn *conn, + struct ksmbd_share_config *share, + struct smb_com_tconx_rsp_ext *rsp) +{ + int length; + char *buf = rsp->Service; + + if (test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { + length = strlen(SERVICE_IPC_SHARE); + memcpy(buf, SERVICE_IPC_SHARE, length); + rsp->ByteCount = cpu_to_le16(length + 1); + buf += length; + *buf = '\0'; + } else { + int uni_len = 0; + + length = strlen(SERVICE_DISK_SHARE); + memcpy(buf, SERVICE_DISK_SHARE, length); + buf[length] = '\0'; + length += 1; + uni_len = smbConvertToUTF16((__le16 *)(buf + length), + NATIVE_FILE_SYSTEM, + PATH_MAX, conn->local_nls, 0); + uni_len++; + uni_len *= 2; + length += uni_len; + rsp->ByteCount = cpu_to_le16(length); + } +} + +/** + * smb_tree_connect_andx() - tree connect request handler + * @work: smb work containing tree connect request buffer + * + * Return: 0 on success, otherwise error + */ +int smb_tree_connect_andx(struct ksmbd_work *work) +{ + struct smb_hdr *req_hdr = (struct smb_hdr *)work->request_buf; + struct smb_hdr *rsp_hdr = (struct smb_hdr *)work->response_buf; + struct ksmbd_conn *conn = work->conn; + struct smb_com_tconx_req *req; + struct smb_com_tconx_rsp_ext *rsp; + int extra_byte = 0; + char *treename = NULL, *name = NULL, *dev_type = NULL; + struct ksmbd_share_config *share; + struct ksmbd_session *sess = work->sess; + int dev_flags = 0; + struct ksmbd_tree_conn_status status; + + /* Is this an ANDX command ? */ + if (req_hdr->Command != SMB_COM_TREE_CONNECT_ANDX) { + ksmbd_debug(SMB, "SMB_COM_TREE_CONNECT_ANDX is part of ANDX"); + req = (struct smb_com_tconx_req *) + andx_request_buffer(work->request_buf, + SMB_COM_TREE_CONNECT_ANDX); + rsp = (struct smb_com_tconx_rsp_ext *) + andx_response_buffer(work->response_buf); + extra_byte = 3; + if (!req) { + status.ret = -EINVAL; + goto out_err; + } + } else { + req = (struct smb_com_tconx_req *)(&req_hdr->WordCount); + rsp = (struct smb_com_tconx_rsp_ext *)(&rsp_hdr->WordCount); + } + + /* check if valid tree name is present in request or not */ + if (!req->PasswordLength) { + treename = smb_strndup_from_utf16(req->Password + 1, + 256, true, conn->local_nls); + dev_type = smb_strndup_from_utf16(req->Password + 1 + + ((strlen(treename) + 1) * 2), 256, false, + conn->local_nls); + } else { + treename = smb_strndup_from_utf16(req->Password + + le16_to_cpu(req->PasswordLength), 256, true, + conn->local_nls); + dev_type = smb_strndup_from_utf16(req->Password + + le16_to_cpu(req->PasswordLength) + + ((strlen(treename) + 1) * 2), + 256, false, conn->local_nls); + } + + if (IS_ERR(treename) || IS_ERR(dev_type)) { + pr_err("Unable to strdup() treename or devtype uid %d\n", + rsp_hdr->Uid); + status.ret = KSMBD_TREE_CONN_STATUS_ERROR; + goto out_err; + } + name = ksmbd_extract_sharename(conn->um, treename); + if (IS_ERR(name)) { + status.ret = KSMBD_TREE_CONN_STATUS_ERROR; + goto out_err; + } + + ksmbd_debug(SMB, "tree connect request for tree %s, dev_type : %s\n", + name, dev_type); + + if (!strcmp(dev_type, "A:")) + dev_flags = 1; + else if (!strncmp(dev_type, "LPT", 3)) + dev_flags = 2; + else if (!strcmp(dev_type, "IPC")) + dev_flags = 3; + else if (!strcmp(dev_type, "COMM")) + dev_flags = 4; + else if (!strcmp(dev_type, "?????")) + dev_flags = 5; + + if (!strcmp(name, "IPC$")) { + if (dev_flags < 3) { + status.ret = -ENODEV; + goto out_err; + } + } else if (!dev_flags || (dev_flags > 1 && dev_flags < 5)) { + status.ret = -ENODEV; + goto out_err; + } + + status = ksmbd_tree_conn_connect(conn, sess, name); + if (status.ret == KSMBD_TREE_CONN_STATUS_OK) + rsp_hdr->Tid = cpu_to_le16(status.tree_conn->id); + else + goto out_err; + + status.ret = 0; + share = status.tree_conn->share_conf; + rsp->WordCount = 7; + rsp->OptionalSupport = 0; + + rsp->OptionalSupport = cpu_to_le16((SMB_SUPPORT_SEARCH_BITS | + SMB_CSC_NO_CACHING | SMB_UNIQUE_FILE_NAME)); + + rsp->MaximalShareAccessRights = cpu_to_le32(FILE_READ_RIGHTS | + FILE_EXEC_RIGHTS); + if (test_tree_conn_flag(status.tree_conn, + KSMBD_TREE_CONN_FLAG_WRITABLE)) + rsp->MaximalShareAccessRights |= cpu_to_le32(FILE_WRITE_RIGHTS); + rsp->GuestMaximalShareAccessRights = 0; + + set_service_type(conn, share, rsp); + + /* For each extra andx response, we have to add 1 byte, + * for wc and 2 bytes for byte count + */ + inc_rfc1001_len(rsp_hdr, + 7 * 2 + le16_to_cpu(rsp->ByteCount) + extra_byte); + + /* this is an ANDx command ? */ + rsp->AndXReserved = 0; + rsp->AndXOffset = cpu_to_le16(get_rfc1002_len(rsp_hdr)); + if (req->AndXCommand != SMB_NO_MORE_ANDX_COMMAND) { + /* adjust response */ + rsp->AndXCommand = req->AndXCommand; + /* More processing required */ + status.ret = rsp->AndXCommand; + } else { + rsp->AndXCommand = SMB_NO_MORE_ANDX_COMMAND; + } + + kfree(treename); + kfree(dev_type); + kfree(name); + + return status.ret; + +out_err: + if (!IS_ERR(treename)) + kfree(treename); + if (!IS_ERR(dev_type)) + kfree(dev_type); + if (!IS_ERR(name)) + kfree(name); + + rsp->WordCount = 7; + rsp->AndXCommand = SMB_NO_MORE_ANDX_COMMAND; + rsp->AndXReserved = 0; + rsp->AndXOffset = cpu_to_le16(get_rfc1002_len(rsp_hdr)); + rsp->OptionalSupport = 0; + rsp->MaximalShareAccessRights = 0; + rsp->GuestMaximalShareAccessRights = 0; + rsp->ByteCount = 0; + ksmbd_debug(SMB, "error while tree connect\n"); + switch (status.ret) { + case KSMBD_TREE_CONN_STATUS_NO_SHARE: + rsp_hdr->Status.CifsError = STATUS_BAD_NETWORK_PATH; + break; + case -ENOMEM: + case KSMBD_TREE_CONN_STATUS_NOMEM: + rsp_hdr->Status.CifsError = STATUS_NO_MEMORY; + break; + case KSMBD_TREE_CONN_STATUS_TOO_MANY_CONNS: + case KSMBD_TREE_CONN_STATUS_TOO_MANY_SESSIONS: + rsp_hdr->Status.CifsError = STATUS_ACCESS_DENIED; + break; + case -ENODEV: + rsp_hdr->Status.CifsError = STATUS_BAD_DEVICE_TYPE; + break; + case KSMBD_TREE_CONN_STATUS_ERROR: + rsp_hdr->Status.CifsError = STATUS_BAD_NETWORK_NAME; + break; + case -EINVAL: + rsp_hdr->Status.CifsError = STATUS_INVALID_PARAMETER; + break; + default: + rsp_hdr->Status.CifsError = STATUS_ACCESS_DENIED; + } + + inc_rfc1001_len(rsp_hdr, (7 * 2 + le16_to_cpu(rsp->ByteCount) + + extra_byte)); + return -EINVAL; +} + +/** + * smb_get_name() - convert filename on smb packet to char string + * @src: source filename, mostly in unicode format + * @maxlen: maxlen of src string to be used for parsing + * @work: smb work containing smb header flag + * @converted: src string already converted to local characterset + * + * Return: pointer to filename string on success, otherwise error ptr + */ +static char * +smb_get_name(struct ksmbd_share_config *share, const char *src, + const int maxlen, struct ksmbd_work *work, bool converted) +{ + struct smb_hdr *req_hdr = (struct smb_hdr *)work->request_buf; + bool is_unicode = is_smbreq_unicode(req_hdr); + char *name, *wild_card_pos; + + if (converted) + name = (char *)src; + else { + name = smb_strndup_from_utf16(src, maxlen, is_unicode, + work->conn->local_nls); + if (IS_ERR(name)) { + ksmbd_debug(SMB, "failed to get name %ld\n", + PTR_ERR(name)); + return name; + } + } + + ksmbd_conv_path_to_unix(name); + ksmbd_strip_last_slash(name); + + /*Handling of dir path in FIND_FIRST2 having '*' at end of path*/ + wild_card_pos = strrchr(name, '*'); + + if (wild_card_pos != NULL) + *wild_card_pos = '\0'; + + + if (ksmbd_validate_filename(name) < 0) + return ERR_PTR(-ENOENT); + + if (ksmbd_share_veto_filename(share, name)) { + ksmbd_debug(SMB, + "file(%s) open is not allowed by setting as veto file\n", + name); + if (!converted) + kfree(name); + return ERR_PTR(-ENOENT); + } + + ksmbd_debug(SMB, "file name = %s\n", name); + + return name; +} + +/** + * smb_get_dir_name() - convert directory name on smb packet to char string + * @src: source dir name, mostly in unicode format + * @maxlen: maxlen of src string to be used for parsing + * @work: smb work containing smb header flag + * @srch_ptr: update search pointer in dir for searching dir entries + * + * Return: pointer to dir name string on success, otherwise error ptr + */ +static char *smb_get_dir_name(struct ksmbd_share_config *share, const char *src, + const int maxlen, struct ksmbd_work *work, char **srch_ptr) +{ + struct smb_hdr *req_hdr = (struct smb_hdr *)work->request_buf; + struct smb_hdr *rsp_hdr = (struct smb_hdr *)work->response_buf; + bool is_unicode = is_smbreq_unicode(req_hdr); + char *name, *pattern_pos, *pattern = NULL; + int pattern_len, rc; + + name = smb_strndup_from_utf16(src, maxlen, is_unicode, + work->conn->local_nls); + if (IS_ERR(name)) { + pr_err("failed to allocate memory\n"); + rsp_hdr->Status.CifsError = STATUS_NO_MEMORY; + return name; + } + + ksmbd_conv_path_to_unix(name); + ksmbd_strip_last_slash(name); + + pattern_pos = strrchr(name, '/'); + + if (!pattern_pos) + pattern_pos = name; + else + pattern_pos += 1; + + pattern_len = strlen(pattern_pos); + if (pattern_len == 0) { + rc = -EINVAL; + goto err_name; + } + ksmbd_debug(SMB, "pattern searched = %s pattern_len = %d\n", + pattern_pos, pattern_len); + pattern = kmalloc(pattern_len + 1, GFP_KERNEL); + if (!pattern) { + rc = -ENOMEM; + goto err_name; + } + memcpy(pattern, pattern_pos, pattern_len); + *(pattern + pattern_len) = '\0'; + *pattern_pos = '\0'; + *srch_ptr = pattern; + + if (ksmbd_validate_filename(name) < 0) { + rc = -ENOENT; + goto err_pattern; + } + + if (ksmbd_share_veto_filename(share, name)) { + ksmbd_debug(SMB, + "file(%s) open is not allowed by setting as veto file\n", + name); + rc = -ENOENT; + goto err_pattern; + } + + ksmbd_debug(SMB, "dir name = %s\n", name); + return name; + +err_pattern: + kfree(pattern); +err_name: + kfree(name); + + if (rc == -EINVAL) + rsp_hdr->Status.CifsError = STATUS_INVALID_PARAMETER; + else if (rc == -ENOMEM) + rsp_hdr->Status.CifsError = STATUS_NO_MEMORY; + else if (rc == -ENOENT) + rsp_hdr->Status.CifsError = STATUS_OBJECT_NAME_INVALID; + + return ERR_PTR(rc); +} + +/** + * smb_rename() - rename request handler + * @work: smb work containing rename request buffer + * + * Return: 0 on success, otherwise error + */ +int smb_rename(struct ksmbd_work *work) +{ + struct smb_com_rename_req *req = work->request_buf; + struct smb_com_rename_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + bool is_unicode = is_smbreq_unicode(&req->hdr); + char *oldname, *newname; + struct ksmbd_file *fp = NULL; + int oldname_len; + struct path path; + bool file_present = true; + int rc = 0; + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "returning as user does not have permission to write\n"); + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + return -EACCES; + } + + oldname = smb_get_name(share, req->OldFileName, PATH_MAX, work, false); + if (IS_ERR(oldname)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(oldname); + } + + if (is_unicode) + oldname_len = smb1_utf16_name_length((__le16 *)req->OldFileName, + PATH_MAX); + else { + oldname_len = strlen(oldname); + oldname_len++; + } + + newname = smb_get_name(share, &req->OldFileName[oldname_len + 2], + PATH_MAX, work, false); + if (IS_ERR(newname)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + rc = PTR_ERR(newname); + newname = NULL; + goto out; + } + + rc = ksmbd_vfs_kern_path(work, newname, LOOKUP_NO_SYMLINKS, &path, 1); + if (rc) + file_present = false; + else + path_put(&path); + + if (file_present && strncmp(oldname, newname, strlen(oldname))) { + rc = -EEXIST; + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_COLLISION; + ksmbd_debug(SMB, "cannot rename already existing file\n"); + goto out; + } + + ksmbd_debug(SMB, "rename %s -> %s\n", oldname, newname); + rc = ksmbd_vfs_kern_path(work, oldname, LOOKUP_NO_SYMLINKS, &path, 1); + if (rc) + goto out; + + fp = ksmbd_vfs_dentry_open(work, &path, O_RDONLY, 0, false); + if (IS_ERR(fp)) { + fp = NULL; + path_put(&path); + goto out; + } + + rc = ksmbd_vfs_fp_rename(work, fp, newname); + if (rc) { + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + path_put(&path); + goto out; + } + path_put(&path); + rsp->hdr.WordCount = 0; + rsp->ByteCount = 0; +out: + if (fp) + ksmbd_close_fd(work, fp->volatile_id); + kfree(oldname); + kfree(newname); + return rc; +} + +/** + * smb_handle_negotiate() - negotiate request handler + * @work: smb work containing negotiate request buffer + * + * Return: 0 on success, otherwise error + */ +int smb_handle_negotiate(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb_negotiate_rsp *neg_rsp = work->response_buf; + __u64 time; + int rc = 0; + + WARN_ON(ksmbd_conn_good(work)); + + if (conn->dialect == BAD_PROT_ID) { + neg_rsp->hdr.Status.CifsError = STATUS_INVALID_LOGON_TYPE; + rc = -EINVAL; + goto err_out; + } + + conn->connection_type = 0; + + /* wct 17 for NTLM */ + neg_rsp->hdr.WordCount = 17; + neg_rsp->DialectIndex = cpu_to_le16(conn->dialect); + + neg_rsp->SecurityMode = SMB1_SERVER_SECU; + if (server_conf.signing == KSMBD_CONFIG_OPT_AUTO || + server_conf.signing == KSMBD_CONFIG_OPT_MANDATORY) { + conn->sign = true; + neg_rsp->SecurityMode |= SECMODE_SIGN_ENABLED; + if (server_conf.signing == KSMBD_CONFIG_OPT_MANDATORY) + neg_rsp->SecurityMode |= SECMODE_SIGN_REQUIRED; + } + neg_rsp->MaxMpxCount = cpu_to_le16(SMB1_MAX_MPX_COUNT); + neg_rsp->MaxNumberVcs = cpu_to_le16(SMB1_MAX_VCS); + neg_rsp->MaxBufferSize = cpu_to_le32(conn->vals->max_read_size); + neg_rsp->MaxRawSize = cpu_to_le32(SMB1_MAX_RAW_SIZE); + neg_rsp->SessionKey = 0; + neg_rsp->Capabilities = cpu_to_le32(SMB1_SERVER_CAPS); + + time = ksmbd_systime(); + neg_rsp->SystemTimeLow = cpu_to_le32(time & 0x00000000FFFFFFFF); + neg_rsp->SystemTimeHigh = + cpu_to_le32((time & 0xFFFFFFFF00000000) >> 32); + neg_rsp->ServerTimeZone = 0; + + if (((struct smb_hdr *)work->request_buf)->Flags2 & SMBFLG2_EXT_SEC) + conn->use_spnego = true; + + ksmbd_debug(SMB, "spnego is %s\n", conn->use_spnego ? "on" : "off"); + + if (conn->use_spnego == false) { + neg_rsp->EncryptionKeyLength = CIFS_CRYPTO_KEY_SIZE; + neg_rsp->Capabilities &= ~cpu_to_le32(CAP_EXTENDED_SECURITY); + neg_rsp->ByteCount = cpu_to_le16(CIFS_CRYPTO_KEY_SIZE); + /* initialize random server challenge */ + get_random_bytes(conn->ntlmssp.cryptkey, sizeof(__u64)); + memcpy((neg_rsp->u.EncryptionKey), conn->ntlmssp.cryptkey, + CIFS_CRYPTO_KEY_SIZE); + /* Adjust pdu length, 17 words and 8 bytes added */ + inc_rfc1001_len(neg_rsp, (17 * 2 + 8)); + } else { + neg_rsp->EncryptionKeyLength = 0; + neg_rsp->ByteCount = cpu_to_le16(SMB1_CLIENT_GUID_SIZE + + AUTH_GSS_LENGTH); + get_random_bytes(neg_rsp->u.extended_response.GUID, + SMB1_CLIENT_GUID_SIZE); + ksmbd_copy_gss_neg_header( + neg_rsp->u.extended_response.SecurityBlob); + inc_rfc1001_len(neg_rsp, (17 * 2 + 16 + AUTH_GSS_LENGTH)); + } + + /* Null terminated domain name in unicode */ + + ksmbd_conn_set_need_negotiate(work); + /* Domain name and PC name are ignored by clients, so no need to send. + * We can try sending them later + */ +err_out: + return rc; +} + +static int build_sess_rsp_noextsec(struct ksmbd_conn *conn, + struct ksmbd_session *sess, + struct smb_com_session_setup_req_no_secext *req, + struct smb_com_session_setup_old_resp *rsp) +{ + int offset, err = 0, len; + char *name; + __le16 str[32]; + + /* Build response. We don't use extended security (yet), so wct is 3 */ + rsp->hdr.WordCount = 3; + rsp->Action = 0; + /* The names should be unicode */ + rsp->ByteCount = 0; + /* adjust pdu length. data added 6 bytes */ + inc_rfc1001_len(&rsp->hdr, 6); + + /* check if valid user name is present in request or not */ + offset = le16_to_cpu(req->CaseInsensitivePasswordLength) + + le16_to_cpu(req->CaseSensitivePasswordLength); + + /* 1 byte for padding */ + name = smb_strndup_from_utf16((req->CaseInsensitivePassword + offset + + 1), 256, true, conn->local_nls); + if (IS_ERR(name)) { + pr_err("cannot allocate memory\n"); + err = PTR_ERR(name); + goto out_err; + } + + WARN_ON(sess->user); + + ksmbd_debug(SMB, "session setup request for user %s\n", name); + sess->user = ksmbd_login_user(name); + kfree(name); + if (!sess->user) { + pr_err("user not present in database\n"); + err = -EINVAL; + goto out_err; + } + + if (user_guest(sess->user)) { + rsp->Action = cpu_to_le16(GUEST_LOGIN); + goto no_password_check; + } + + if (le16_to_cpu(req->CaseSensitivePasswordLength) == + CIFS_AUTH_RESP_SIZE) { + err = ksmbd_auth_ntlm(sess, req->CaseInsensitivePassword + + le16_to_cpu(req->CaseInsensitivePasswordLength), + conn->ntlmssp.cryptkey); + if (err) { + pr_err("ntlm authentication failed for user %s\n", + user_name(sess->user)); + goto out_err; + } + } else { + char *ntdomain; + + offset = le16_to_cpu(req->CaseInsensitivePasswordLength) + + le16_to_cpu(req->CaseSensitivePasswordLength) + + ((strlen(user_name(sess->user)) + 1) * 2); + + ntdomain = smb_strndup_from_utf16( + req->CaseInsensitivePassword + + offset + 1, 256, true, conn->local_nls); + if (IS_ERR(ntdomain)) { + pr_err("cannot allocate memory\n"); + err = PTR_ERR(ntdomain); + goto out_err; + } + + err = ksmbd_auth_ntlmv2(conn, sess, + (struct ntlmv2_resp *) ((char *) + req->CaseInsensitivePassword + + le16_to_cpu(req->CaseInsensitivePasswordLength)), + le16_to_cpu(req->CaseSensitivePasswordLength) - + CIFS_ENCPWD_SIZE, ntdomain, + conn->ntlmssp.cryptkey); + kfree(ntdomain); + if (err) { + pr_err("authentication failed for user %s\n", + user_name(sess->user)); + goto out_err; + } + } + +no_password_check: + /* this is an ANDx command ? */ + rsp->AndXReserved = 0; + rsp->AndXOffset = cpu_to_le16(get_rfc1002_len(&rsp->hdr)); + + /* 1 byte padding for word alignment */ + offset = 1; + + memset(str, 0 , sizeof(str)); + + len = smb_strtoUTF16(str, "Unix", 4, conn->local_nls); + len = UNICODE_LEN(len + 1); + memcpy(rsp->NativeOS + offset, str, len); + offset += len; + + len = smb_strtoUTF16(str, "ksmbd", 5, conn->local_nls); + len = UNICODE_LEN(len + 1); + memcpy(rsp->NativeOS + offset, str, len); + offset += len; + + len = smb_strtoUTF16(str, "WORKGROUP", 9, conn->local_nls); + len = UNICODE_LEN(len + 1); + memcpy(rsp->NativeOS + offset, str, len); + offset += len; + + rsp->ByteCount = cpu_to_le16(offset); + inc_rfc1001_len(&rsp->hdr, offset); + + if (req->AndXCommand != SMB_NO_MORE_ANDX_COMMAND) { + /* adjust response */ + rsp->AndXCommand = req->AndXCommand; + return rsp->AndXCommand; /* More processing required */ + } + rsp->AndXCommand = SMB_NO_MORE_ANDX_COMMAND; + +out_err: + return err; +} + +static int build_sess_rsp_extsec(struct ksmbd_conn *conn, + struct ksmbd_session *sess, + struct smb_com_session_setup_req *req, + struct smb_com_session_setup_resp *rsp) +{ + struct negotiate_message *negblob; + char *neg_blob; + int err = 0, neg_blob_len; + unsigned char *spnego_blob; + u16 spnego_blob_len; + int sz; + + rsp->hdr.WordCount = 4; + rsp->Action = 0; + + /* The names should be unicode */ + rsp->ByteCount = 0; + /* adjust pdu length. data added 6 bytes */ + inc_rfc1001_len(&rsp->hdr, 8); + + negblob = (struct negotiate_message *)req->SecurityBlob; + sz = le16_to_cpu(req->SecurityBlobLength); + + if (ksmbd_decode_negTokenInit((char *)negblob, sz, conn)) { + if (ksmbd_decode_negTokenTarg((char *)negblob, sz, conn)) { + conn->use_spnego = false; + } + } + + if (conn->mechToken) + negblob = (struct negotiate_message *)conn->mechToken; + + if (negblob->MessageType == NtLmNegotiate) { + struct challenge_message *chgblob; + + ksmbd_debug(SMB, "negotiate phase\n"); + err = ksmbd_decode_ntlmssp_neg_blob(negblob, + le16_to_cpu(req->SecurityBlobLength), + conn); + if (err) + goto out_err; + + chgblob = (struct challenge_message *)rsp->SecurityBlob; + memset(chgblob, 0, sizeof(struct challenge_message)); + + if (conn->use_spnego) { + int sz; + + sz = sizeof(struct negotiate_message) + + (strlen(ksmbd_netbios_name()) * 2 + 1 + 4) * 6; + neg_blob = kmalloc(sz, GFP_KERNEL); + if (!neg_blob) { + err = -ENOMEM; + goto out_err; + } + chgblob = (struct challenge_message *)neg_blob; + neg_blob_len = ksmbd_build_ntlmssp_challenge_blob( + chgblob, + conn); + if (neg_blob_len < 0) { + kfree(neg_blob); + err = -ENOMEM; + goto out_err; + } + + if (build_spnego_ntlmssp_neg_blob(&spnego_blob, + &spnego_blob_len, + neg_blob, neg_blob_len)) { + kfree(neg_blob); + err = -ENOMEM; + goto out_err; + } + + memcpy((char *)rsp->SecurityBlob, spnego_blob, + spnego_blob_len); + rsp->SecurityBlobLength = + cpu_to_le16(spnego_blob_len); + kfree(spnego_blob); + kfree(neg_blob); + } else { + neg_blob_len = ksmbd_build_ntlmssp_challenge_blob( + chgblob, + conn); + if (neg_blob_len < 0) { + err = -ENOMEM; + goto out_err; + } + + rsp->SecurityBlobLength = cpu_to_le16(neg_blob_len); + } + + rsp->hdr.Status.CifsError = STATUS_MORE_PROCESSING_REQUIRED; + /* + * Note: here total size -1 is done as an adjustment + * for 0 size blob. + */ + inc_rfc1001_len(rsp, le16_to_cpu(rsp->SecurityBlobLength)); + rsp->ByteCount = rsp->SecurityBlobLength; + } else if (negblob->MessageType == NtLmAuthenticate) { + struct authenticate_message *authblob; + char *username; + + ksmbd_debug(SMB, "authenticate phase\n"); + if (conn->use_spnego && conn->mechToken) + authblob = + (struct authenticate_message *)conn->mechToken; + else + authblob = (struct authenticate_message *) + req->SecurityBlob; + + username = smb_strndup_from_utf16((const char *)authblob + + le32_to_cpu(authblob->UserName.BufferOffset), + le16_to_cpu(authblob->UserName.Length), true, + conn->local_nls); + + if (IS_ERR(username)) { + pr_err("cannot allocate memory\n"); + err = PTR_ERR(username); + goto out_err; + } + + ksmbd_debug(SMB, "session setup request for user %s\n", + username); + sess->user = ksmbd_login_user(username); + kfree(username); + + if (!sess->user) { + ksmbd_debug(SMB, "Unknown user name or an error\n"); + err = -EINVAL; + goto out_err; + } + + if (user_guest(sess->user)) { + rsp->Action = cpu_to_le16(GUEST_LOGIN); + goto no_password_check; + } + + err = ksmbd_decode_ntlmssp_auth_blob(authblob, + le16_to_cpu(req->SecurityBlobLength), + conn, sess); + if (err) { + ksmbd_debug(SMB, "authentication failed\n"); + err = -EINVAL; + goto out_err; + } + +no_password_check: + if (conn->use_spnego) { + if (build_spnego_ntlmssp_auth_blob(&spnego_blob, + &spnego_blob_len, 0)) { + err = -ENOMEM; + goto out_err; + } + + memcpy((char *)rsp->SecurityBlob, spnego_blob, + spnego_blob_len); + rsp->SecurityBlobLength = + cpu_to_le16(spnego_blob_len); + kfree(spnego_blob); + inc_rfc1001_len(rsp, spnego_blob_len); + rsp->ByteCount = rsp->SecurityBlobLength; + } + } else { + pr_err("Invalid phase\n"); + err = -EINVAL; + } + + /* this is an ANDx command ? */ + rsp->AndXReserved = 0; + rsp->AndXOffset = cpu_to_le16(get_rfc1002_len(&rsp->hdr)); + + if (req->AndXCommand != SMB_NO_MORE_ANDX_COMMAND) { + /* adjust response */ + rsp->AndXCommand = req->AndXCommand; + return rsp->AndXCommand; /* More processing required */ + } + rsp->AndXCommand = SMB_NO_MORE_ANDX_COMMAND; + +out_err: + if (conn->use_spnego && conn->mechToken) { + kfree(conn->mechToken); + conn->mechToken = NULL; + } + + return err; +} + +/** + * smb_session_setup_andx() - session setup request handler + * @work: smb work containing session setup request buffer + * + * Return: 0 on success, otherwise error + */ +int smb_session_setup_andx(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = NULL; + int rc = 0, cap; + unsigned short uid; + + union smb_com_session_setup_andx *pSMB = work->request_buf; + union smb_com_session_setup_andx *rsp = work->response_buf; + + if (pSMB->req.hdr.WordCount == 12) + cap = le32_to_cpu(pSMB->req.Capabilities); + else if (pSMB->req.hdr.WordCount == 13) + cap = le32_to_cpu(pSMB->req_no_secext.Capabilities); + else { + pr_err("malformed packet\n"); + work->send_no_response = 1; + return 0; + } + + uid = le16_to_cpu(pSMB->req.hdr.Uid); + if (uid != 0) { + sess = ksmbd_session_lookup(conn, uid); + if (!sess) { + rc = -ENOENT; + goto out_err; + } + ksmbd_debug(SMB, "Reuse session ID: %llu, Uid: %u\n", + sess->id, uid); + } else { + sess = ksmbd_smb1_session_create(); + if (!sess) { + rc = -ENOMEM; + goto out_err; + } + + rc = ksmbd_session_register(conn, sess); + if (rc) + goto out_err; + rsp->resp.hdr.Uid = cpu_to_le16(sess->id); + ksmbd_debug(SMB, "New session ID: %llu, Uid: %u\n", sess->id, + uid); + } + + if (cap & CAP_EXTENDED_SECURITY) { + ksmbd_debug(SMB, "build response with extend_security\n"); + rc = build_sess_rsp_extsec(conn, sess, &pSMB->req, &rsp->resp); + + } else { + ksmbd_debug(SMB, "build response without extend_security\n"); + rc = build_sess_rsp_noextsec(conn, sess, &pSMB->req_no_secext, + &rsp->old_resp); + } + if (rc < 0) + goto out_err; + + work->sess = sess; + ksmbd_conn_set_good(work); + return 0; + +out_err: + rsp->resp.hdr.Status.CifsError = STATUS_LOGON_FAILURE; + rsp->resp.hdr.WordCount = 0; + rsp->resp.ByteCount = 0; + if (rc < 0 && sess) { + xa_erase(&conn->sessions, sess->id); + ksmbd_session_destroy(sess); + work->sess = NULL; + } + return rc; +} + +/** + * file_create_dispostion_flags() - convert disposition flags to + * file open flags + * @dispostion: file disposition contained in open request + * @file_present: file already present or not + * + * Return: file open flags after conversion from disposition + */ +static int file_create_dispostion_flags(int dispostion, bool file_present) +{ + int disp_flags = 0; + + switch (dispostion) { + /* + * If the file already exists, it SHOULD be superseded (overwritten). + * If it does not already exist, then it SHOULD be created. + */ + case FILE_SUPERSEDE: + if (file_present) + disp_flags |= O_TRUNC; + else + disp_flags |= O_CREAT; + break; + /* + * If the file already exists, it SHOULD be opened rather than created. + * If the file does not already exist, the operation MUST fail. + */ + case FILE_OPEN: + if (!file_present) + return -ENOENT; + break; + /* + * If the file already exists, the operation MUST fail. + * If the file does not already exist, it SHOULD be created. + */ + case FILE_CREATE: + if (file_present) + return -EEXIST; + disp_flags |= O_CREAT; + break; + /* + * If the file already exists, it SHOULD be opened. If the file + * does not already exist, then it SHOULD be created. + */ + case FILE_OPEN_IF: + if (!file_present) + disp_flags |= O_CREAT; + break; + /* + * If the file already exists, it SHOULD be opened and truncated. + * If the file does not already exist, the operation MUST fail. + */ + case FILE_OVERWRITE: + if (!file_present) + return -ENOENT; + disp_flags |= O_TRUNC; + break; + /* + * If the file already exists, it SHOULD be opened and truncated. + * If the file does not already exist, it SHOULD be created. + */ + case FILE_OVERWRITE_IF: + if (file_present) + disp_flags |= O_TRUNC; + else + disp_flags |= O_CREAT; + break; + default: + return -EINVAL; + } + + return disp_flags; +} + +static inline int ksmbd_openflags_to_mayflags(int open_flags) +{ + int mask = open_flags & O_ACCMODE; + + if (mask == O_WRONLY) + return MAY_OPEN | MAY_WRITE; + else if (mask == O_RDWR) + return MAY_OPEN | MAY_READ | MAY_WRITE; + else + return MAY_OPEN | MAY_READ; +} + +/** + * convert_generic_access_flags() - convert access flags to + * file open flags + * @access_flag: file access flags contained in open request + * @open_flag: file open flags are updated as per access flags + * @may_flags: file may flags are updated with @open_flags + * @attrib: attribute flag indicating posix symantics or not + * + * Return: access flags + */ +static int +convert_generic_access_flags(int access_flag, int *open_flags, + int *may_flags, int attrib) +{ + int aflags = access_flag; + int oflags = *open_flags; + + if (aflags & GENERIC_READ) { + aflags &= ~GENERIC_READ; + aflags |= GENERIC_READ_FLAGS; + } + + if (aflags & GENERIC_WRITE) { + aflags &= ~GENERIC_WRITE; + aflags |= GENERIC_WRITE_FLAGS; + } + + if (aflags & GENERIC_EXECUTE) { + aflags &= ~GENERIC_EXECUTE; + aflags |= GENERIC_EXECUTE_FLAGS; + } + + if (aflags & GENERIC_ALL) { + aflags &= ~GENERIC_ALL; + aflags |= GENERIC_ALL_FLAGS; + } + + if (oflags & O_TRUNC) + aflags |= FILE_WRITE_DATA; + + if (aflags & (FILE_WRITE_DATA | FILE_APPEND_DATA)) { + if (aflags & (FILE_READ_ATTRIBUTES | FILE_READ_DATA | + FILE_READ_EA | FILE_EXECUTE)) { + *open_flags |= O_RDWR; + + } else { + *open_flags |= O_WRONLY; + } + } else { + *open_flags |= O_RDONLY; + } + + if ((attrib & ATTR_POSIX_SEMANTICS) && (aflags & FILE_APPEND_DATA)) + *open_flags |= O_APPEND; + + *may_flags = ksmbd_openflags_to_mayflags(*open_flags); + + return aflags; +} + +/** + * smb_get_dos_attr() - convert unix style stat info to dos attr + * @stat: stat to be converted to dos attr + * + * Return: dos style attribute + */ +static __u32 smb_get_dos_attr(struct kstat *stat) +{ + __u32 attr = 0; + + /* check whether file has attributes ATTR_READONLY, ATTR_HIDDEN, + * ATTR_SYSTEM, ATTR_VOLUME, ATTR_DIRECTORY, ATTR_ARCHIVE, + * ATTR_DEVICE, ATTR_NORMAL, ATTR_TEMPORARY, ATTR_SPARSE, + * ATTR_REPARSE, ATTR_COMPRESSED, ATTR_OFFLINE + */ + + if (stat->mode & S_ISVTX) /* hidden */ + attr |= (ATTR_HIDDEN | ATTR_SYSTEM); + + if (!(stat->mode & 0222)) /* read-only */ + attr |= ATTR_READONLY; + + if (S_ISDIR(stat->mode)) + attr |= ATTR_DIRECTORY; + + if (stat->size > (stat->blksize * stat->blocks)) + attr |= ATTR_SPARSE; + + if (!attr) + attr |= ATTR_NORMAL; + + return attr; +} + +static int +lock_oplock_release(struct ksmbd_file *fp, int type, int oplock_level) +{ + struct oplock_info *opinfo; + int ret; + + ksmbd_debug(SMB, "got oplock brk for level OplockLevel = %d\n", + oplock_level); + + opinfo = fp->f_opinfo; + if (opinfo->op_state == OPLOCK_STATE_NONE) { + pr_err("unexpected oplock state 0x%x\n", opinfo->op_state); + return -EINVAL; + } + + if (oplock_level == OPLOCK_EXCLUSIVE || oplock_level == OPLOCK_BATCH) { + if (opinfo_write_to_none(opinfo) < 0) { + opinfo->op_state = OPLOCK_STATE_NONE; + return -EINVAL; + } + } else if (((opinfo->level == OPLOCK_EXCLUSIVE) || + (opinfo->level == OPLOCK_BATCH)) && + (oplock_level == OPLOCK_READ)) { + ret = opinfo_write_to_read(opinfo); + if (ret) { + opinfo->op_state = OPLOCK_STATE_NONE; + return -EINVAL; + } + } else if ((opinfo->level == OPLOCK_READ) && + (oplock_level == OPLOCK_NONE)) { + ret = opinfo_read_to_none(opinfo); + if (ret) { + opinfo->op_state = OPLOCK_STATE_NONE; + return -EINVAL; + } + } + + opinfo->op_state = OPLOCK_STATE_NONE; + wake_up_interruptible(&opinfo->oplock_q); + + return 0; +} + +static struct ksmbd_lock *smb_lock_init(struct file_lock *flock, + unsigned int cmd, int mode, unsigned long long offset, + unsigned long long length, struct list_head *lock_list) +{ + struct ksmbd_lock *lock; + + lock = kzalloc(sizeof(struct ksmbd_lock), GFP_KERNEL); + if (!lock) + return NULL; + + lock->cmd = cmd; + lock->fl = flock; + lock->start = offset; + lock->end = offset + length; + lock->flags = mode; + if (lock->start == lock->end) + lock->zero_len = 1; + INIT_LIST_HEAD(&lock->llist); + INIT_LIST_HEAD(&lock->clist); + INIT_LIST_HEAD(&lock->flist); + list_add_tail(&lock->llist, lock_list); + + return lock; +} + +/** + * smb_locking_andx() - received oplock break response from client + * @work: smb work containing oplock break command + * + * Return: 0 on success, otherwise error + */ +int smb_locking_andx(struct ksmbd_work *work) +{ + struct smb_com_lock_req *req = work->request_buf; + struct smb_com_lock_rsp *rsp = work->response_buf; + struct ksmbd_file *fp; + int err = 0; + struct locking_andx_range32 *lock_ele32 = NULL, *unlock_ele32 = NULL; + struct locking_andx_range64 *lock_ele64 = NULL, *unlock_ele64 = NULL; + struct file *filp = NULL; + struct ksmbd_lock *smb_lock = NULL, *cmp_lock, *tmp, *tmp2; + int i, lock_count, unlock_count; + unsigned long long offset, length; + struct file_lock *flock = NULL; + unsigned int cmd = 0; + LIST_HEAD(lock_list); + LIST_HEAD(rollback_list); + int locked, timeout; + const unsigned long long loff_max = ~0; + struct ksmbd_conn *conn; + + timeout = le32_to_cpu(req->Timeout); + ksmbd_debug(SMB, "got oplock brk for fid %d lock type = 0x%x, timeout : %d\n", + req->Fid, req->LockType, timeout); + + /* find fid */ + fp = ksmbd_lookup_fd_fast(work, req->Fid); + if (!fp) { + pr_err("cannot obtain fid for %d\n", req->Fid); + return -EINVAL; + } + + if (req->LockType & LOCKING_ANDX_OPLOCK_RELEASE) { + pr_err("lock type is oplock release\n"); + err = lock_oplock_release(fp, req->LockType, req->OplockLevel); + } + + filp = fp->filp; + lock_count = le16_to_cpu(req->NumberOfLocks); + unlock_count = le16_to_cpu(req->NumberOfUnlocks); + + ksmbd_debug(SMB, "lock count is %d, unlock_count : %d\n", + lock_count, unlock_count); + + if (req->LockType & LOCKING_ANDX_LARGE_FILES) + lock_ele64 = (struct locking_andx_range64 *)req->Locks; + else + lock_ele32 = (struct locking_andx_range32 *)req->Locks; + + if (req->LockType & LOCKING_ANDX_CHANGE_LOCKTYPE) { + pr_err("lock type: LOCKING_ANDX_CHANGE_LOCKTYPE\n"); + rsp->hdr.Status.DosError.ErrorClass = ERRDOS; + rsp->hdr.Status.DosError.Error = cpu_to_le16(ERRnoatomiclocks); + rsp->hdr.Flags2 &= ~SMBFLG2_ERR_STATUS; + goto out; + } + + if (req->LockType & LOCKING_ANDX_CANCEL_LOCK) + pr_err("lock type: LOCKING_ANDX_CANCEL_LOCK\n"); + + for (i = 0; i < lock_count; i++) { + flock = smb_flock_init(filp); + if (!flock) + goto out; + + if (req->LockType & LOCKING_ANDX_SHARED_LOCK) { + pr_err("received shared request\n"); + if (!(filp->f_mode & FMODE_READ)) { + rsp->hdr.Status.CifsError = + STATUS_ACCESS_DENIED; + locks_free_lock(flock); + goto out; + } + cmd = F_SETLKW; + flock->fl_type = F_RDLCK; + } else { + pr_err("received exclusive request\n"); + if (!(filp->f_mode & FMODE_WRITE)) { + rsp->hdr.Status.CifsError = + STATUS_ACCESS_DENIED; + locks_free_lock(flock); + goto out; + } + cmd = F_SETLKW; + flock->fl_type = F_WRLCK; + flock->fl_flags |= FL_SLEEP; + } + + if (req->LockType & LOCKING_ANDX_LARGE_FILES) { + offset = (unsigned long long)le32_to_cpu( + lock_ele64[i].OffsetLow); + length = (unsigned long long)le32_to_cpu( + lock_ele64[i].LengthLow); + offset |= (unsigned long long)le32_to_cpu( + lock_ele64[i].OffsetHigh) << 32; + length |= (unsigned long long)le32_to_cpu( + lock_ele64[i].LengthHigh) << 32; + } else { + offset = (unsigned long long)le32_to_cpu( + lock_ele32[i].Offset); + length = (unsigned long long)le32_to_cpu( + lock_ele32[i].Length); + } + + if (offset > loff_max) { + pr_err("Invalid lock range requested\n"); + rsp->hdr.Status.CifsError = STATUS_INVALID_LOCK_RANGE; + locks_free_lock(flock); + goto out; + } + + if (offset > 0 && length > (loff_max - offset) + 1) { + pr_err("Invalid lock range requested\n"); + rsp->hdr.Status.CifsError = STATUS_INVALID_LOCK_RANGE; + locks_free_lock(flock); + goto out; + } + + ksmbd_debug(SMB, "locking offset : %llx, length : %llu\n", + offset, length); + + if (offset > OFFSET_MAX) + flock->fl_start = OFFSET_MAX; + else + flock->fl_start = offset; + if (offset + length > OFFSET_MAX) + flock->fl_end = OFFSET_MAX; + else + flock->fl_end = offset + length; + + smb_lock = smb_lock_init(flock, cmd, req->LockType, offset, + length, &lock_list); + if (!smb_lock) { + locks_free_lock(flock); + goto out; + } + } + + list_for_each_entry_safe(smb_lock, tmp, &lock_list, llist) { + int same_zero_lock = 0; + + list_del(&smb_lock->llist); + /* check locks in connections */ + read_lock(&conn_list_lock); + list_for_each_entry(conn, &conn_list, conns_list) { + spin_lock(&conn->llist_lock); + list_for_each_entry_safe(cmp_lock, tmp2, &conn->lock_list, clist) { + if (file_inode(cmp_lock->fl->fl_file) != + file_inode(smb_lock->fl->fl_file)) + continue; + + if (smb_lock->zero_len && + cmp_lock->start == smb_lock->start && + cmp_lock->end == smb_lock->end) { + same_zero_lock = 1; + spin_unlock(&conn->llist_lock); + read_unlock(&conn_list_lock); + goto out_check_cl; + } + + /* check zero byte lock range */ + if (cmp_lock->zero_len && !smb_lock->zero_len && + cmp_lock->start > smb_lock->start && + cmp_lock->start < smb_lock->end) { + pr_err("previous lock conflict with zero byte lock range\n"); + err = -EPERM; + } else if (smb_lock->zero_len && !cmp_lock->zero_len && + smb_lock->start > cmp_lock->start && + smb_lock->start < cmp_lock->end) { + pr_err("current lock conflict with zero byte lock range\n"); + err = -EPERM; + } else if (((cmp_lock->start <= smb_lock->start && + cmp_lock->end > smb_lock->start) || + (cmp_lock->start < smb_lock->end && + cmp_lock->end >= smb_lock->end)) && + !cmp_lock->zero_len && !smb_lock->zero_len) { + pr_err("Not allow lock operation on exclusive lock range\n"); + err = -EPERM; + } + + if (err) { + /* Clean error cache */ + if ((smb_lock->zero_len && + fp->cflock_cnt > 1) || + (timeout && (fp->llock_fstart == + smb_lock->start))) { + ksmbd_debug(SMB, "clean error cache\n"); + fp->cflock_cnt = 0; + } + + if (timeout > 0 || + (fp->cflock_cnt > 0 && + fp->llock_fstart == smb_lock->start) || + ((smb_lock->start >> 63) == 0 && + smb_lock->start >= 0xEF000000)) { + if (timeout) { + spin_unlock(&conn->llist_lock); + read_unlock(&conn_list_lock); + ksmbd_debug(SMB, "waiting error response for timeout : %d\n", + timeout); + msleep(timeout); + } + rsp->hdr.Status.CifsError = + STATUS_FILE_LOCK_CONFLICT; + } else + rsp->hdr.Status.CifsError = + STATUS_LOCK_NOT_GRANTED; + fp->cflock_cnt++; + fp->llock_fstart = smb_lock->start; + + if (timeout <= 0) { + spin_unlock(&conn->llist_lock); + read_unlock(&conn_list_lock); + } + goto out; + } + } + spin_unlock(&conn->llist_lock); + } + read_unlock(&conn_list_lock); + +out_check_cl: + if (same_zero_lock) + continue; + if (smb_lock->zero_len) { + err = 0; + goto skip; + } + + flock = smb_lock->fl; +retry: + err = vfs_lock_file(filp, smb_lock->cmd, flock, NULL); + if (err == FILE_LOCK_DEFERRED) { + pr_err("would have to wait for getting lock\n"); + spin_lock(&work->conn->llist_lock); + list_add_tail(&smb_lock->clist, + &work->conn->lock_list); + spin_unlock(&work->conn->llist_lock); + list_add(&smb_lock->llist, &rollback_list); +wait: + err = ksmbd_vfs_posix_lock_wait_timeout(flock, + msecs_to_jiffies(10)); + if (err) { + list_del(&smb_lock->llist); + spin_lock(&work->conn->llist_lock); + list_del(&smb_lock->clist); + spin_unlock(&work->conn->llist_lock); + goto retry; + } else + goto wait; + } else if (!err) { +skip: + spin_lock(&work->conn->llist_lock); + list_add_tail(&smb_lock->clist, + &work->conn->lock_list); + list_add_tail(&smb_lock->flist, + &fp->lock_list); + spin_unlock(&work->conn->llist_lock); + list_add(&smb_lock->llist, &rollback_list); + pr_err("successful in taking lock\n"); + } else if (err < 0) { + rsp->hdr.Status.CifsError = STATUS_LOCK_NOT_GRANTED; + goto out; + } + } + + if (req->LockType & LOCKING_ANDX_LARGE_FILES) + unlock_ele64 = (struct locking_andx_range64 *)(req->Locks + + (sizeof(struct locking_andx_range64) * + lock_count)); + else + unlock_ele32 = (struct locking_andx_range32 *)(req->Locks + + (sizeof(struct locking_andx_range32) * + lock_count)); + + for (i = 0; i < unlock_count; i++) { + flock = smb_flock_init(filp); + if (!flock) + goto out; + + flock->fl_type = F_UNLCK; + cmd = 0; + + if (req->LockType & LOCKING_ANDX_LARGE_FILES) { + offset = (unsigned long long)le32_to_cpu( + unlock_ele64[i].OffsetLow); + length = (unsigned long long)le32_to_cpu( + unlock_ele64[i].LengthLow); + offset |= (unsigned long long)le32_to_cpu( + unlock_ele64[i].OffsetHigh) << 32; + length |= (unsigned long long)le32_to_cpu( + unlock_ele64[i].LengthHigh) << 32; + } else { + offset = (unsigned long long)le32_to_cpu( + unlock_ele32[i].Offset); + length = (unsigned long long)le32_to_cpu( + unlock_ele32[i].Length); + } + + ksmbd_debug(SMB, "unlock offset : %llx, length : %llu\n", + offset, length); + + if (offset > OFFSET_MAX) + flock->fl_start = OFFSET_MAX; + else + flock->fl_start = offset; + if (offset + length > OFFSET_MAX) + flock->fl_end = OFFSET_MAX; + else + flock->fl_end = offset + length; + + locked = 0; + read_lock(&conn_list_lock); + list_for_each_entry(conn, &conn_list, conns_list) { + spin_lock(&conn->llist_lock); + list_for_each_entry(cmp_lock, &conn->lock_list, clist) { + if (file_inode(cmp_lock->fl->fl_file) != + file_inode(flock->fl_file)) + continue; + + if ((cmp_lock->start == offset && + cmp_lock->end == offset + length)) { + locked = 1; + spin_unlock(&conn->llist_lock); + read_unlock(&conn_list_lock); + goto out_check_cl_unlck; + } + } + spin_unlock(&conn->llist_lock); + } + read_unlock(&conn_list_lock); + +out_check_cl_unlck: + if (!locked) { + locks_free_lock(flock); + rsp->hdr.Status.CifsError = STATUS_RANGE_NOT_LOCKED; + goto out; + } + + err = vfs_lock_file(filp, cmd, flock, NULL); + if (!err) { + ksmbd_debug(SMB, "File unlocked\n"); + spin_lock(&conn->llist_lock); + if (!list_empty(&cmp_lock->flist)) + list_del(&cmp_lock->flist); + list_del(&cmp_lock->clist); + spin_unlock(&conn->llist_lock); + + locks_free_lock(cmp_lock->fl); + kfree(cmp_lock); + fp->cflock_cnt = 0; + } else if (err == -ENOENT) { + rsp->hdr.Status.CifsError = STATUS_RANGE_NOT_LOCKED; + locks_free_lock(flock); + goto out; + } + locks_free_lock(flock); + } + + rsp->hdr.WordCount = 2; + rsp->ByteCount = 0; + inc_rfc1001_len(&rsp->hdr, (rsp->hdr.WordCount * 2)); + + /* this is an ANDx command ? */ + rsp->AndXReserved = 0; + rsp->AndXOffset = cpu_to_le16(get_rfc1002_len(&rsp->hdr)); + if (req->AndXCommand != SMB_NO_MORE_ANDX_COMMAND) { + /* adjust response */ + rsp->AndXCommand = req->AndXCommand; + return rsp->AndXCommand; /* More processing required */ + } + rsp->AndXCommand = SMB_NO_MORE_ANDX_COMMAND; + ksmbd_fd_put(work, fp); + return err; + +out: + list_for_each_entry_safe(smb_lock, tmp, &lock_list, llist) { + locks_free_lock(smb_lock->fl); + list_del(&smb_lock->llist); + kfree(smb_lock); + } + + list_for_each_entry_safe(smb_lock, tmp, &rollback_list, llist) { + struct file_lock *rlock = NULL; + + rlock = smb_flock_init(filp); + rlock->fl_type = F_UNLCK; + rlock->fl_start = smb_lock->start; + rlock->fl_end = smb_lock->end; + + err = vfs_lock_file(filp, 0, rlock, NULL); + if (err) + pr_err("rollback unlock fail : %d\n", err); + + list_del(&smb_lock->llist); + spin_lock(&work->conn->llist_lock); + if (!list_empty(&smb_lock->flist)) + list_del(&smb_lock->flist); + list_del(&smb_lock->clist); + spin_unlock(&work->conn->llist_lock); + + locks_free_lock(smb_lock->fl); + locks_free_lock(rlock); + kfree(smb_lock); + } + + ksmbd_fd_put(work, fp); + pr_err("failed in taking lock\n"); + return err; +} + +/** + * smb_trans() - trans2 command dispatcher + * @work: smb work containing trans2 command + * + * Return: 0 on success, otherwise error + */ +int smb_trans(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb_com_trans_req *req = work->request_buf; + struct smb_com_trans_rsp *rsp = work->response_buf; + struct smb_com_trans_pipe_req *pipe_req = work->request_buf; + struct ksmbd_rpc_command *rpc_resp; + __u16 subcommand; + char *name, *pipe; + char *pipedata; + int setup_bytes_count = 0; + int pipe_name_offset = 0; + int str_len_uni; + int ret = 0, nbytes = 0; + int param_len = 0; + int id; + int padding; + + if (req->SetupCount) + setup_bytes_count = 2 * req->SetupCount; + + subcommand = le16_to_cpu(req->SubCommand); + name = smb_strndup_from_utf16(req->Data + setup_bytes_count, 256, 1, + conn->local_nls); + + if (IS_ERR(name)) { + pr_err("failed to allocate memory\n"); + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + return PTR_ERR(name); + } + + ksmbd_debug(SMB, "Obtained string name = %s setupcount = %d\n", + name, setup_bytes_count); + + pipe_name_offset = strlen("\\PIPE"); + if (strncmp("\\PIPE", name, pipe_name_offset) != 0) { + ksmbd_debug(SMB, "Not Pipe request\n"); + rsp->hdr.Status.CifsError = STATUS_NOT_SUPPORTED; + kfree(name); + return 0; + } + + if (name[pipe_name_offset] == '\\') + pipe_name_offset++; + + pipe = name + pipe_name_offset; + + if (*pipe != '\0' && strncmp(pipe, "LANMAN", sizeof("LANMAN")) != 0) { + ksmbd_debug(SMB, "Pipe %s not supported request\n", pipe); + rsp->hdr.Status.CifsError = STATUS_NOT_SUPPORTED; + kfree(name); + return 0; + } + + /* Incoming pipe name unicode len */ + str_len_uni = 2 * (strlen(name) + 1); + + ksmbd_debug(SMB, "Pipe name unicode len = %d\n", str_len_uni); + + /* Some clients like Windows may have additional padding. */ + padding = le16_to_cpu(req->ParameterOffset) - + offsetof(struct smb_com_trans_req, Data) + - str_len_uni; + pipedata = req->Data + str_len_uni + setup_bytes_count + padding; + + if (!strncmp(pipe, "LANMAN", sizeof("LANMAN"))) { + rpc_resp = ksmbd_rpc_rap(work->sess, pipedata, + le16_to_cpu(req->TotalParameterCount)); + + if (rpc_resp) { + if (rpc_resp->flags == KSMBD_RPC_ENOTIMPLEMENTED) { + rsp->hdr.Status.CifsError = + STATUS_NOT_SUPPORTED; + kvfree(rpc_resp); + goto out; + } else if (rpc_resp->flags != KSMBD_RPC_OK) { + rsp->hdr.Status.CifsError = + STATUS_INVALID_PARAMETER; + kvfree(rpc_resp); + goto out; + } + + nbytes = rpc_resp->payload_sz; + memcpy((char *)rsp + sizeof(struct smb_com_trans_rsp), + rpc_resp->payload, nbytes); + + kvfree(rpc_resp); + ret = 0; + goto resp_out; + } else { + ret = -EINVAL; + goto out; + } + } + + id = pipe_req->fid; + switch (subcommand) { + case TRANSACT_DCERPCCMD: + + ksmbd_debug(SMB, "GOT TRANSACT_DCERPCCMD\n"); + ret = -EINVAL; + rpc_resp = ksmbd_rpc_ioctl(work->sess, id, pipedata, + le16_to_cpu(req->DataCount)); + if (rpc_resp) { + if (rpc_resp->flags == KSMBD_RPC_ENOTIMPLEMENTED) { + rsp->hdr.Status.CifsError = + STATUS_NOT_SUPPORTED; + kvfree(rpc_resp); + goto out; + } else if (rpc_resp->flags != KSMBD_RPC_OK) { + rsp->hdr.Status.CifsError = + STATUS_INVALID_PARAMETER; + kvfree(rpc_resp); + goto out; + } + + nbytes = rpc_resp->payload_sz; + memcpy((char *)rsp + sizeof(struct smb_com_trans_rsp), + rpc_resp->payload, nbytes); + kvfree(rpc_resp); + ret = 0; + } + break; + + default: + ksmbd_debug(SMB, "SMB TRANS subcommand not supported %u\n", + subcommand); + ret = -EOPNOTSUPP; + rsp->hdr.Status.CifsError = STATUS_NOT_SUPPORTED; + goto out; + } + +resp_out: + + rsp->hdr.WordCount = 10; + rsp->TotalParameterCount = cpu_to_le16(param_len); + rsp->TotalDataCount = cpu_to_le16(nbytes); + rsp->Reserved = 0; + rsp->ParameterCount = cpu_to_le16(param_len); + rsp->ParameterOffset = cpu_to_le16(56); + rsp->ParameterDisplacement = 0; + rsp->DataCount = cpu_to_le16(nbytes); + rsp->DataOffset = cpu_to_le16(56 + param_len); + rsp->DataDisplacement = 0; + rsp->SetupCount = 0; + rsp->Reserved1 = 0; + /* Adding 1 for Pad */ + rsp->ByteCount = cpu_to_le16(nbytes + 1 + param_len); + rsp->Pad = 0; + inc_rfc1001_len(&rsp->hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + +out: + kfree(name); + return ret; +} + +/** + * create_andx_pipe() - create ipc pipe request handler + * @work: smb work containing create command + * + * Return: 0 on success, otherwise error + */ +static int create_andx_pipe(struct ksmbd_work *work) +{ + struct smb_com_open_req *req = work->request_buf; + struct smb_com_open_ext_rsp *rsp = work->response_buf; + char *name; + int rc = 0; + __u16 fid; + + /* one byte pad before unicode file name start */ + if (is_smbreq_unicode(&req->hdr)) + name = smb_strndup_from_utf16(req->fileName + 1, 256, 1, + work->conn->local_nls); + else + name = smb_strndup_from_utf16(req->fileName, 256, 1, + work->conn->local_nls); + + if (IS_ERR(name)) { + rc = -ENOMEM; + goto out; + } + + rc = ksmbd_session_rpc_open(work->sess, name); + if (rc < 0) + goto out; + fid = rc; + + rsp->hdr.WordCount = 42; + rsp->AndXCommand = SMB_NO_MORE_ANDX_COMMAND; + rsp->AndXReserved = 0; + rsp->OplockLevel = 0; + rsp->Fid = fid; + rsp->CreateAction = cpu_to_le32(1); + rsp->CreationTime = 0; + rsp->LastAccessTime = 0; + rsp->LastWriteTime = 0; + rsp->ChangeTime = 0; + rsp->FileAttributes = cpu_to_le32(ATTR_NORMAL); + rsp->AllocationSize = cpu_to_le64(0); + rsp->EndOfFile = 0; + rsp->FileType = cpu_to_le16(2); + rsp->DeviceState = cpu_to_le16(0x05ff); + rsp->DirectoryFlag = 0; + rsp->fid = 0; + rsp->MaxAccess = cpu_to_le32(FILE_GENERIC_ALL); + rsp->GuestAccess = cpu_to_le32(FILE_GENERIC_READ); + rsp->ByteCount = 0; + inc_rfc1001_len(&rsp->hdr, 100); + +out: + switch (rc) { + case 0: + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + break; + case -EINVAL: + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + break; + case -ENOSPC: + case -ENOMEM: + default: + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + break; + } + + kfree(name); + return rc; +} + +/** + * smb_nt_create_andx() - file open request handler + * @work: smb work containing nt open command + * + * Return: 0 on success, otherwise error + */ +int smb_nt_create_andx(struct ksmbd_work *work) +{ + struct smb_com_open_req *req = work->request_buf; + struct smb_com_open_rsp *rsp = work->response_buf; + struct smb_com_open_ext_rsp *ext_rsp = work->response_buf; + struct ksmbd_conn *conn = work->conn; + struct ksmbd_tree_connect *tcon = work->tcon; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct path path; + struct kstat stat; + int oplock_flags, file_info, open_flags, may_flags, access_flags; + char *name; + char *conv_name; + bool file_present = true, extended_reply; + __u64 alloc_size = 0, time; + umode_t mode = 0; + int err; + int create_directory = 0; + char *src; + char *root = NULL; + bool is_unicode; + bool is_relative_root = false; + struct ksmbd_file *fp = NULL; + int oplock_rsp = OPLOCK_NONE; + int share_ret; + + rsp->hdr.Status.CifsError = STATUS_UNSUCCESSFUL; + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "create pipe on IPC\n"); + return create_andx_pipe(work); + } + + if (req->CreateOptions & FILE_OPEN_BY_FILE_ID_LE) { + ksmbd_debug(SMB, "file open with FID is not supported\n"); + rsp->hdr.Status.CifsError = STATUS_NOT_SUPPORTED; + return -EINVAL; + } + + if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) { + if (req->DesiredAccess && + !(le32_to_cpu(req->DesiredAccess) & DELETE)) { + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + return -EPERM; + } + + if (le32_to_cpu(req->FileAttributes) & ATTR_READONLY) { + rsp->hdr.Status.CifsError = STATUS_CANNOT_DELETE; + return -EPERM; + } + } + + if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { + ksmbd_debug(SMB, "GOT Create Directory via CREATE ANDX\n"); + create_directory = 1; + } + + /* + * Filename is relative to this root directory FID, instead of + * tree connect point. Find root dir name from this FID and + * prepend root dir name in filename. + */ + if (req->RootDirectoryFid) { + ksmbd_debug(SMB, "path lookup relative to RootDirectoryFid\n"); + + is_relative_root = true; + fp = ksmbd_lookup_fd_fast(work, req->RootDirectoryFid); + if (fp) + root = (char *)fp->filp->f_path.dentry->d_name.name; + else { + rsp->hdr.Status.CifsError = STATUS_INVALID_HANDLE; + memset(&rsp->hdr.WordCount, 0, 3); + return -EINVAL; + } + ksmbd_fd_put(work, fp); + } + + /* here allocated +2 (UNI '\0') length for both ASCII & UNI + * to avoid unnecessary if/else check + */ + src = kzalloc(le16_to_cpu(req->NameLength) + 2, GFP_KERNEL); + if (!src) { + rsp->hdr.Status.CifsError = + STATUS_NO_MEMORY; + + return -ENOMEM; + } + + if (is_smbreq_unicode(&req->hdr)) { + memcpy(src, req->fileName + 1, le16_to_cpu(req->NameLength)); + is_unicode = true; + } else { + memcpy(src, req->fileName, le16_to_cpu(req->NameLength)); + is_unicode = false; + } + + name = smb_strndup_from_utf16(src, PATH_MAX, is_unicode, + conn->local_nls); + kfree(src); + + if (IS_ERR(name)) { + if (PTR_ERR(name) == -ENOMEM) { + pr_err("failed to allocate memory\n"); + rsp->hdr.Status.CifsError = + STATUS_NO_MEMORY; + } else + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + + return PTR_ERR(name); + } + + if (is_relative_root) { + char *full_name; + + full_name = kasprintf(GFP_KERNEL, "\\%s\\%s", root, name); + if (!full_name) { + kfree(name); + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + return -ENOMEM; + } + + kfree(name); + name = full_name; + } + + root = strrchr(name, '\\'); + if (root) { + root++; + if ((root[0] == '*' || root[0] == '/') && (root[1] == '\0')) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + kfree(name); + return -EINVAL; + } + } + + conv_name = smb_get_name(share, name, PATH_MAX, work, true); + if (IS_ERR(conv_name)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + kfree(name); + return PTR_ERR(conv_name); + } + + if (ksmbd_override_fsids(work)) { + err = -ENOMEM; + goto out1; + } + + err = ksmbd_vfs_kern_path(work, conv_name, LOOKUP_NO_SYMLINKS, &path, + (req->hdr.Flags & SMBFLG_CASELESS) && + !create_directory); + if (err) { + if (err == -EACCES || err == -EXDEV) + goto out; + file_present = false; + ksmbd_debug(SMB, "can not get linux path for %s, err = %d\n", + conv_name, err); + } else { + if (d_is_symlink(path.dentry)) { + err = -EACCES; + goto free_path; + } + + err = vfs_getattr(&path, &stat, STATX_BASIC_STATS, + AT_STATX_SYNC_AS_STAT); + if (err) { + pr_err("can not stat %s, err = %d\n", + conv_name, err); + goto free_path; + } + } + + if (file_present && (req->CreateOptions & FILE_NON_DIRECTORY_FILE_LE) && + S_ISDIR(stat.mode)) { + ksmbd_debug(SMB, "Can't open dir %s, request is to open file\n", + conv_name); + if (!(((struct smb_hdr *)work->request_buf)->Flags2 & + SMBFLG2_ERR_STATUS)) { + rsp->hdr.Status.DosError.ErrorClass = ERRDOS; + rsp->hdr.Status.DosError.Error = + cpu_to_le16(ERRfilexists); + } else + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_COLLISION; + + memset(&rsp->hdr.WordCount, 0, 3); + + goto free_path; + } + + if (file_present && create_directory && !S_ISDIR(stat.mode)) { + ksmbd_debug(SMB, "Can't open file %s, request is to open dir\n", + conv_name); + if (!(((struct smb_hdr *)work->request_buf)->Flags2 & + SMBFLG2_ERR_STATUS)) { + ntstatus_to_dos(STATUS_NOT_A_DIRECTORY, + &rsp->hdr.Status.DosError.ErrorClass, + &rsp->hdr.Status.DosError.Error); + } else + rsp->hdr.Status.CifsError = + STATUS_NOT_A_DIRECTORY; + + memset(&rsp->hdr.WordCount, 0, 3); + + goto free_path; + } + + oplock_flags = le32_to_cpu(req->OpenFlags) & + (REQ_OPLOCK | REQ_BATCHOPLOCK); + extended_reply = le32_to_cpu(req->OpenFlags) & REQ_EXTENDED_INFO; + open_flags = file_create_dispostion_flags( + le32_to_cpu(req->CreateDisposition), file_present); + + if (open_flags < 0) { + ksmbd_debug(SMB, "create_dispostion returned %d\n", open_flags); + if (file_present) { + if (!(((struct smb_hdr *)work->request_buf)->Flags2 & + SMBFLG2_ERR_STATUS)) { + rsp->hdr.Status.DosError.ErrorClass = ERRDOS; + rsp->hdr.Status.DosError.Error = + cpu_to_le16(ERRfilexists); + } else if (open_flags == -EINVAL) + rsp->hdr.Status.CifsError = + STATUS_INVALID_PARAMETER; + else + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_COLLISION; + memset(&rsp->hdr.WordCount, 0, 3); + goto free_path; + } else { + err = -ENOENT; + goto out; + } + } else { + if (file_present) { + if (S_ISFIFO(stat.mode)) + open_flags |= O_NONBLOCK; + } + + if (req->CreateOptions & FILE_WRITE_THROUGH_LE) + open_flags |= O_SYNC; + } + + access_flags = convert_generic_access_flags( + le32_to_cpu(req->DesiredAccess), + &open_flags, &may_flags, + le32_to_cpu(req->FileAttributes)); + + mode |= 0777; + if (le32_to_cpu(req->FileAttributes) & ATTR_READONLY) + mode &= ~0222; + + /* TODO: + * - check req->ShareAccess for sharing file among different process + * - check req->FileAttributes for special/readonly file attrib + * - check req->SecurityFlags for client security context tracking + * - check req->ImpersonationLevel + */ + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + if (open_flags & O_CREAT) { + ksmbd_debug(SMB, + "returning as user does not have permission to write\n"); + err = -EACCES; + goto out; + } + } + + ksmbd_debug(SMB, "filename : %s, open_flags = 0x%x\n", conv_name, + open_flags); + if (!file_present && (open_flags & O_CREAT)) { + + if (!create_directory) { + mode |= S_IFREG; + err = ksmbd_vfs_create(work, conv_name, mode); + if (err) + goto out; + } else { + err = ksmbd_vfs_mkdir(work, conv_name, mode); + if (err) { + pr_err("Can't create directory %s", + conv_name); + goto out; + } + } + + err = ksmbd_vfs_kern_path(work, conv_name, 0, &path, 0); + if (err) { + pr_err("cannot get linux path, err = %d\n", err); + goto out; + } + } else { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + err = inode_permission(mnt_user_ns(path.mnt), + d_inode(path.dentry), + may_flags); +#else + err = inode_permission(d_inode(path.dentry), + may_flags); +#endif + if (err) + goto free_path; + } + + err = ksmbd_query_inode_status(d_inode(path.dentry->d_parent)); + if (err == KSMBD_INODE_STATUS_PENDING_DELETE) { + err = -EBUSY; + goto free_path; + } + + err = 0; + /* open file and get FID */ + fp = ksmbd_vfs_dentry_open(work, + &path, + open_flags, + req->CreateOptions, + file_present); + if (IS_ERR(fp)) { + err = PTR_ERR(fp); + fp = NULL; + goto free_path; + } + fp->daccess = req->DesiredAccess; + fp->saccess = req->ShareAccess; + fp->pid = le16_to_cpu(req->hdr.Pid); + + write_lock(&fp->f_ci->m_lock); + list_add(&fp->node, &fp->f_ci->m_fp_list); + write_unlock(&fp->f_ci->m_lock); + + share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp); + if (smb1_oplock_enable && + test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_OPLOCKS) && + !S_ISDIR(file_inode(fp->filp)->i_mode) && oplock_flags) { + /* Client cannot request levelII oplock directly */ + err = smb_grant_oplock(work, oplock_flags, fp->volatile_id, + fp, le16_to_cpu(req->hdr.Tid), NULL, share_ret); + if (err) + goto free_path; + } else { + if (ksmbd_inode_pending_delete(fp)) { + err = -EBUSY; + goto free_path; + } + + if (share_ret < 0) { + err = -EPERM; + goto free_path; + } + } + + oplock_rsp = fp->f_opinfo != NULL ? fp->f_opinfo->level : 0; + + if (file_present) { + if (!(open_flags & O_TRUNC)) + file_info = F_OPENED; + else + file_info = F_OVERWRITTEN; + } else + file_info = F_CREATED; + + if (le32_to_cpu(req->DesiredAccess) & (DELETE | GENERIC_ALL)) + fp->is_nt_open = 1; + if ((le32_to_cpu(req->DesiredAccess) & DELETE) && + (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) + ksmbd_fd_set_delete_on_close(fp, file_info); + + /* open success, send back response */ + err = vfs_getattr(&path, &stat, STATX_BASIC_STATS, + AT_STATX_SYNC_AS_STAT); + if (err) { + pr_err("cannot get stat information\n"); + goto free_path; + } + + alloc_size = le64_to_cpu(req->AllocationSize); + if (alloc_size && (file_info == F_CREATED || + file_info == F_OVERWRITTEN)) { + if (alloc_size > stat.size) { + err = ksmbd_vfs_truncate(work, fp, alloc_size); + if (err) { + pr_err("failed to expand file, err = %d\n", + err); + goto free_path; + } + } + } + + /* prepare response buffer */ + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + + rsp->OplockLevel = oplock_rsp; + rsp->Fid = fp->volatile_id; + + if ((le32_to_cpu(req->CreateDisposition) == FILE_SUPERSEDE) && + (file_info == F_OVERWRITTEN)) + rsp->CreateAction = cpu_to_le32(F_SUPERSEDED); + else + rsp->CreateAction = cpu_to_le32(file_info); + + if (stat.result_mask & STATX_BTIME) + fp->create_time = ksmbd_UnixTimeToNT(stat.btime); + else + fp->create_time = ksmbd_UnixTimeToNT(stat.ctime); + if (file_present) { + if (test_share_config_flag(tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + struct xattr_dos_attrib da; + + err = ksmbd_vfs_get_dos_attrib_xattr(mnt_user_ns(path.mnt), + path.dentry, &da); + if (err > 0) + fp->create_time = da.create_time; + err = 0; + } + } else { + if (test_share_config_flag(tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + struct xattr_dos_attrib da = {0}; + + da.version = 4; + da.attr = smb_get_dos_attr(&stat); + da.create_time = fp->create_time; + + err = ksmbd_vfs_set_dos_attrib_xattr(mnt_user_ns(path.mnt), + path.dentry, &da); + if (err) + ksmbd_debug(SMB, "failed to store creation time in xattr\n"); + err = 0; + } + } + + rsp->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(stat.atime); + rsp->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.mtime); + rsp->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.ctime); + rsp->ChangeTime = cpu_to_le64(time); + + rsp->FileAttributes = cpu_to_le32(smb_get_dos_attr(&stat)); + rsp->AllocationSize = cpu_to_le64(stat.blocks << 9); + rsp->EndOfFile = cpu_to_le64(stat.size); + /* TODO: is it normal file, named pipe, printer, modem etc*/ + rsp->FileType = 0; + /* status of named pipe*/ + rsp->DeviceState = 0; + rsp->DirectoryFlag = S_ISDIR(stat.mode) ? 1 : 0; + if (extended_reply) { + struct inode *inode; + + rsp->hdr.WordCount = 50; + memset(&ext_rsp->VolId, 0, 16); + if (fp) { + inode = file_inode(fp->filp); + ext_rsp->fid = inode->i_ino; + if (S_ISDIR(inode->i_mode) || + (fp->filp->f_mode & FMODE_WRITE)) + ext_rsp->MaxAccess = FILE_GENERIC_ALL_LE; + else + ext_rsp->MaxAccess = FILE_GENERIC_READ_LE | + FILE_EXECUTE_LE; + } else { + ext_rsp->MaxAccess = FILE_GENERIC_ALL_LE; + ext_rsp->fid = 0; + } + + ext_rsp->ByteCount = 0; + + } else { + rsp->hdr.WordCount = 34; + rsp->ByteCount = 0; + } + inc_rfc1001_len(&rsp->hdr, (rsp->hdr.WordCount * 2 + 0)); + +free_path: + path_put(&path); +out: + ksmbd_revert_fsids(work); +out1: + switch (err) { + case 0: + break; + case -ENOSPC: + rsp->hdr.Status.CifsError = STATUS_DISK_FULL; + break; + case -EMFILE: + rsp->hdr.Status.CifsError = + STATUS_TOO_MANY_OPENED_FILES; + break; + case -EINVAL: + rsp->hdr.Status.CifsError = STATUS_NO_SUCH_USER; + break; + case -EACCES: + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + break; + case -EPERM: + rsp->hdr.Status.CifsError = STATUS_SHARING_VIOLATION; + break; + case -ENOENT: + rsp->hdr.Status.CifsError = STATUS_OBJECT_NAME_NOT_FOUND; + break; + case -EBUSY: + rsp->hdr.Status.CifsError = STATUS_DELETE_PENDING; + break; + default: + rsp->hdr.Status.CifsError = + STATUS_UNEXPECTED_IO_ERROR; + } + + if (err && fp) + ksmbd_close_fd(work, fp->volatile_id); + + kfree(conv_name); + + if (!rsp->hdr.WordCount) + return err; + + /* this is an ANDx command ? */ + rsp->AndXReserved = 0; + rsp->AndXOffset = cpu_to_le16(get_rfc1002_len(&rsp->hdr)); + if (req->AndXCommand != SMB_NO_MORE_ANDX_COMMAND) { + /* adjust response */ + rsp->AndXCommand = req->AndXCommand; + return rsp->AndXCommand; /* More processing required */ + } + rsp->AndXCommand = SMB_NO_MORE_ANDX_COMMAND; + + return err; +} + +/** + * smb_close_pipe() - ipc pipe close request handler + * @work: smb work containing close command + * + * Return: 0 on success, otherwise error + */ +static int smb_close_pipe(struct ksmbd_work *work) +{ + struct smb_com_close_req *req = work->request_buf; + + ksmbd_session_rpc_close(work->sess, req->FileID); + return 0; +} + +/** + * smb_close() - ipc pipe close request handler + * @work: smb work containing close command + * + * Return: 0 on success, otherwise error + */ +int smb_close(struct ksmbd_work *work) +{ + struct smb_com_close_req *req = work->request_buf; + struct smb_com_close_rsp *rsp = work->response_buf; + int err = 0; + + ksmbd_debug(SMB, "SMB_COM_CLOSE called for fid %u\n", req->FileID); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) { + err = smb_close_pipe(work); + if (err < 0) + goto out; + goto IPC_out; + } + + /* + * TODO: linux cifs client does not send LastWriteTime, + * need to check if windows client use this field + */ + if (le32_to_cpu(req->LastWriteTime) > 0 && + le32_to_cpu(req->LastWriteTime) < 0xFFFFFFFF) + pr_info("need to set last modified time before close\n"); + + err = ksmbd_close_fd(work, req->FileID); + +IPC_out: + /* file close success, return response to server */ + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 0; + rsp->ByteCount = 0; + +out: + if (err) + rsp->hdr.Status.CifsError = STATUS_INVALID_HANDLE; + return err; +} + +/** + * smb_read_andx_pipe() - read from ipc pipe request handler + * @work: smb work containing read command + * + * Return: 0 on success, otherwise error + */ +static int smb_read_andx_pipe(struct ksmbd_work *work) +{ + struct smb_com_read_req *req = work->request_buf; + struct smb_com_read_rsp *rsp = work->response_buf; + struct ksmbd_rpc_command *rpc_resp; + char *data_buf; + int ret = 0, nbytes = 0; + unsigned int count; + unsigned int rsp_buflen = MAX_CIFS_SMALL_BUFFER_SIZE - + sizeof(struct smb_com_read_rsp); + + rsp_buflen = min((unsigned int)(MAX_CIFS_SMALL_BUFFER_SIZE - + sizeof(struct smb_com_read_rsp)), rsp_buflen); + + count = min_t(unsigned int, le16_to_cpu(req->MaxCount), rsp_buflen); + data_buf = (char *) (&rsp->ByteCount) + sizeof(rsp->ByteCount); + + rpc_resp = ksmbd_rpc_read(work->sess, req->Fid); + if (rpc_resp) { + if (rpc_resp->flags != KSMBD_RPC_OK || + !rpc_resp->payload_sz) { + rsp->hdr.Status.CifsError = + STATUS_UNEXPECTED_IO_ERROR; + kvfree(rpc_resp); + return -EINVAL; + } + + nbytes = rpc_resp->payload_sz; + memcpy(data_buf, rpc_resp->payload, rpc_resp->payload_sz); + kvfree(rpc_resp); + } else { + ret = -EINVAL; + } + + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 12; + rsp->Remaining = 0; + rsp->DataCompactionMode = 0; + rsp->DataCompactionMode = 0; + rsp->Reserved = 0; + rsp->DataLength = cpu_to_le16(nbytes & 0xFFFF); + rsp->DataOffset = cpu_to_le16(sizeof(struct smb_com_read_rsp) - + sizeof(rsp->hdr.smb_buf_length)); + rsp->DataLengthHigh = cpu_to_le16(nbytes >> 16); + rsp->Reserved2 = 0; + + rsp->ByteCount = cpu_to_le16(nbytes); + inc_rfc1001_len(&rsp->hdr, (rsp->hdr.WordCount * 2 + nbytes)); + + /* this is an ANDx command ? */ + rsp->AndXReserved = 0; + rsp->AndXOffset = cpu_to_le16(get_rfc1002_len(&rsp->hdr)); + if (req->AndXCommand != SMB_NO_MORE_ANDX_COMMAND) { + /* adjust response */ + rsp->AndXCommand = req->AndXCommand; + return rsp->AndXCommand; /* More processing required */ + } + rsp->AndXCommand = SMB_NO_MORE_ANDX_COMMAND; + + return ret; +} + +/** + * smb_read_andx() - read request handler + * @work: smb work containing read command + * + * Return: 0 on success, otherwise error + */ +int smb_read_andx(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb_com_read_req *req = work->request_buf; + struct smb_com_read_rsp *rsp = work->response_buf; + struct ksmbd_file *fp; + loff_t pos; + size_t count; + ssize_t nbytes; + int err = 0; + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) + return smb_read_andx_pipe(work); + + fp = ksmbd_lookup_fd_fast(work, req->Fid); + if (!fp) { + pr_err("failed to get filp for fid %d\n", + req->Fid); + rsp->hdr.Status.CifsError = STATUS_FILE_CLOSED; + return -ENOENT; + } + + pos = le32_to_cpu(req->OffsetLow); + if (req->hdr.WordCount == 12) + pos |= ((loff_t)le32_to_cpu(req->OffsetHigh) << 32); + + count = le16_to_cpu(req->MaxCount); + /* + * It probably seems to be set to 0 or 0xFFFF if MaxCountHigh is + * not supported. If it is 0xFFFF, it is set to a too large value + * and a read fail occurs. If it is 0xFFFF, limit it to not set + * the value. + * + * [MS-SMB] 3.2.4.4.1: + * If the CAP_LARGE_READX bit is set in + * Client.Connection.ServerCapabilities, then the client is allowed to + * issue a read of a size larger than Client.Connection.MaxBufferSize + * using an SMB_COM_READ_ANDX request. + */ + if (conn->vals->capabilities & CAP_LARGE_READ_X && + le32_to_cpu(req->MaxCountHigh) < 0xFFFF) + count |= le32_to_cpu(req->MaxCountHigh) << 16; + else if (count > CIFS_DEFAULT_IOSIZE) { + ksmbd_debug(SMB, "read size(%zu) exceeds max size(%u)\n", + count, CIFS_DEFAULT_IOSIZE); + ksmbd_debug(SMB, "limiting read size to max size(%u)\n", + CIFS_DEFAULT_IOSIZE); + count = CIFS_DEFAULT_IOSIZE; + } + + ksmbd_debug(SMB, "filename %pd, offset %lld, count %zu\n", + fp->filp->f_path.dentry, pos, count); + + work->aux_payload_buf = kvmalloc(count, GFP_KERNEL | __GFP_ZERO); + if (!work->aux_payload_buf) { + err = -ENOMEM; + goto out; + } + + nbytes = ksmbd_vfs_read(work, fp, count, &pos); + if (nbytes < 0) { + err = nbytes; + goto out; + } + + /* read success, prepare response */ + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 12; + rsp->Remaining = 0; + rsp->DataCompactionMode = 0; + rsp->DataCompactionMode = 0; + rsp->Reserved = 0; + rsp->DataLength = cpu_to_le16(nbytes & 0xFFFF); + rsp->DataOffset = cpu_to_le16(sizeof(struct smb_com_read_rsp) - + sizeof(rsp->hdr.smb_buf_length)); + rsp->DataLengthHigh = cpu_to_le16(nbytes >> 16); + rsp->Reserved2 = 0; + + rsp->ByteCount = cpu_to_le16(nbytes); + inc_rfc1001_len(&rsp->hdr, (rsp->hdr.WordCount * 2)); + work->resp_hdr_sz = get_rfc1002_len(rsp) + 4; + work->aux_payload_sz = nbytes; + inc_rfc1001_len(&rsp->hdr, nbytes); + + /* this is an ANDx command ? */ + rsp->AndXReserved = 0; + rsp->AndXOffset = cpu_to_le16(get_rfc1002_len(&rsp->hdr)); + if (req->AndXCommand != SMB_NO_MORE_ANDX_COMMAND) { + /* adjust response */ + rsp->AndXCommand = req->AndXCommand; + ksmbd_fd_put(work, fp); + return rsp->AndXCommand; /* More processing required */ + } + rsp->AndXCommand = SMB_NO_MORE_ANDX_COMMAND; + +out: + ksmbd_fd_put(work, fp); + if (err) + rsp->hdr.Status.CifsError = STATUS_INVALID_HANDLE; + return err; +} + +/** + * smb_write() - write request handler + * @work: smb work containing write command + * + * Return: 0 on success, otherwise error + */ +int smb_write(struct ksmbd_work *work) +{ + struct smb_com_write_req_32bit *req = work->request_buf; + struct smb_com_write_rsp_32bit *rsp = work->response_buf; + struct ksmbd_file *fp = NULL; + loff_t pos; + size_t count; + char *data_buf; + ssize_t nbytes = 0; + int err = 0; + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "returning as user does not have permission to write\n"); + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + return -EACCES; + } + + fp = ksmbd_lookup_fd_fast(work, req->Fid); + if (!fp) { + pr_err("failed to get filp for fid %u\n", req->Fid); + rsp->hdr.Status.CifsError = STATUS_FILE_CLOSED; + return -ENOENT; + } + + pos = le32_to_cpu(req->Offset); + count = le16_to_cpu(req->Length); + data_buf = req->Data; + + ksmbd_debug(SMB, "filename %pd, offset %lld, count %zu\n", + fp->filp->f_path.dentry, pos, count); + if (!count) { + err = ksmbd_vfs_truncate(work, fp, pos); + nbytes = 0; + } else + err = ksmbd_vfs_write(work, fp, data_buf, + count, &pos, 0, &nbytes); + + rsp->hdr.WordCount = 1; + rsp->Written = cpu_to_le16(nbytes & 0xFFFF); + rsp->ByteCount = 0; + inc_rfc1001_len(&rsp->hdr, (rsp->hdr.WordCount * 2)); + + ksmbd_fd_put(work, fp); + if (!err) { + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + return 0; + } + + if (err == -ENOSPC || err == -EFBIG) + rsp->hdr.Status.CifsError = STATUS_DISK_FULL; + else + rsp->hdr.Status.CifsError = STATUS_INVALID_HANDLE; + return err; +} + +/** + * smb_write_andx_pipe() - write on pipe request handler + * @work: smb work containing write command + * + * Return: 0 on success, otherwise error + */ +static int smb_write_andx_pipe(struct ksmbd_work *work) +{ + struct smb_com_write_req *req = work->request_buf; + struct smb_com_write_rsp *rsp = work->response_buf; + struct ksmbd_rpc_command *rpc_resp; + int ret = 0; + size_t count = 0; + + count = le16_to_cpu(req->DataLengthLow); + if (work->conn->vals->capabilities & CAP_LARGE_WRITE_X) + count |= (le16_to_cpu(req->DataLengthHigh) << 16); + + rpc_resp = ksmbd_rpc_write(work->sess, req->Fid, req->Data, count); + if (rpc_resp) { + if (rpc_resp->flags == KSMBD_RPC_ENOTIMPLEMENTED) { + rsp->hdr.Status.CifsError = STATUS_NOT_SUPPORTED; + kvfree(rpc_resp); + return -EOPNOTSUPP; + } + if (rpc_resp->flags != KSMBD_RPC_OK) { + rsp->hdr.Status.CifsError = STATUS_INVALID_HANDLE; + kvfree(rpc_resp); + return -EINVAL; + } + count = rpc_resp->payload_sz; + kvfree(rpc_resp); + } else { + ret = -EINVAL; + } + + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 6; + rsp->Count = cpu_to_le16(count & 0xFFFF); + rsp->Remaining = 0; + rsp->CountHigh = cpu_to_le16(count >> 16); + rsp->Reserved = 0; + rsp->ByteCount = 0; + inc_rfc1001_len(&rsp->hdr, (rsp->hdr.WordCount * 2)); + + /* this is an ANDx command ? */ + rsp->AndXReserved = 0; + rsp->AndXOffset = cpu_to_le16(get_rfc1002_len(&rsp->hdr)); + if (req->AndXCommand != SMB_NO_MORE_ANDX_COMMAND) { + /* adjust response */ + rsp->AndXCommand = req->AndXCommand; + return rsp->AndXCommand; /* More processing required */ + } + rsp->AndXCommand = SMB_NO_MORE_ANDX_COMMAND; + + return ret; +} + +/** + * smb_write_andx() - andx write request handler + * @work: smb work containing write command + * + * Return: 0 on success, otherwise error + */ +int smb_write_andx(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb_com_write_req *req = work->request_buf; + struct smb_com_write_rsp *rsp = work->response_buf; + struct ksmbd_file *fp; + bool writethrough = false; + loff_t pos; + size_t count; + ssize_t nbytes = 0; + char *data_buf; + int err = 0; + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "returning as user does not have permission to write\n"); + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + return -EACCES; + } + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "Write ANDX called for IPC$"); + return smb_write_andx_pipe(work); + } + + fp = ksmbd_lookup_fd_fast(work, req->Fid); + if (!fp) { + pr_err("failed to get filp for fid %u\n", req->Fid); + rsp->hdr.Status.CifsError = STATUS_FILE_CLOSED; + return -ENOENT; + } + + pos = le32_to_cpu(req->OffsetLow); + if (req->hdr.WordCount == 14) + pos |= ((loff_t)le32_to_cpu(req->OffsetHigh) << 32); + + writethrough = (le16_to_cpu(req->WriteMode) == 1); + + /* + * [MS-SMB] 3.3.5.8: + * If CAP_LARGE_WRITEX is set in Server.Connection.ClientCapabilities, + * then it is possible that the count of bytes to be written is larger + * than the server's MaxBufferSize + */ + count = le16_to_cpu(req->DataLengthLow); + if (conn->vals->capabilities & CAP_LARGE_WRITE_X) + count |= (le16_to_cpu(req->DataLengthHigh) << 16); + else if (count > CIFS_DEFAULT_IOSIZE) { + ksmbd_debug(SMB, "write size(%zu) exceeds max size(%u)\n", + count, CIFS_DEFAULT_IOSIZE); + ksmbd_debug(SMB, "limiting write size to max size(%u)\n", + CIFS_DEFAULT_IOSIZE); + count = CIFS_DEFAULT_IOSIZE; + } + + if (le16_to_cpu(req->DataOffset) == + (offsetof(struct smb_com_write_req, Data) - 4)) { + data_buf = (char *)&req->Data[0]; + } else { + if ((le16_to_cpu(req->DataOffset) > get_rfc1002_len(req)) || + (le16_to_cpu(req->DataOffset) + + count > get_rfc1002_len(req))) { + pr_err("invalid write data offset %u, smb_len %u\n", + le16_to_cpu(req->DataOffset), + get_rfc1002_len(req)); + err = -EINVAL; + goto out; + } + + data_buf = (char *)(((char *)&req->hdr.Protocol) + + le16_to_cpu(req->DataOffset)); + } + + ksmbd_debug(SMB, "filname %pd, offset %lld, count %zu\n", + fp->filp->f_path.dentry, pos, count); + err = ksmbd_vfs_write(work, fp, data_buf, count, &pos, + writethrough, &nbytes); + if (err < 0) + goto out; + + /* write success, prepare response */ + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 6; + rsp->Count = cpu_to_le16(nbytes & 0xFFFF); + rsp->Remaining = 0; + rsp->CountHigh = cpu_to_le16(nbytes >> 16); + rsp->Reserved = 0; + rsp->ByteCount = 0; + inc_rfc1001_len(&rsp->hdr, (rsp->hdr.WordCount * 2)); + + ksmbd_fd_put(work, fp); + /* this is an ANDx command ? */ + rsp->AndXReserved = 0; + rsp->AndXOffset = cpu_to_le16(get_rfc1002_len(&rsp->hdr)); + if (req->AndXCommand != SMB_NO_MORE_ANDX_COMMAND) { + /* adjust response */ + rsp->AndXCommand = req->AndXCommand; + return rsp->AndXCommand; /* More processing required */ + } + rsp->AndXCommand = SMB_NO_MORE_ANDX_COMMAND; + + return 0; + +out: + ksmbd_fd_put(work, fp); + if (err == -ENOSPC || err == -EFBIG) + rsp->hdr.Status.CifsError = STATUS_DISK_FULL; + else + rsp->hdr.Status.CifsError = STATUS_INVALID_HANDLE; + return err; +} + +/** + * smb_echo() - echo(ping) request handler + * @work: smb work containing echo command + * + * Return: 0 on success, otherwise error + */ +int smb_echo(struct ksmbd_work *work) +{ + struct smb_com_echo_req *req = work->request_buf; + struct smb_com_echo_rsp *rsp = work->response_buf; + __u16 data_count; + int i; + + ksmbd_debug(SMB, "SMB_COM_ECHO called with echo count %u\n", + le16_to_cpu(req->EchoCount)); + + if (le16_to_cpu(req->EchoCount) > 1) + work->multiRsp = 1; + + data_count = le16_to_cpu(req->ByteCount); + /* send echo response to server */ + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 1; + rsp->ByteCount = cpu_to_le16(data_count); + + memcpy(rsp->Data, req->Data, data_count); + inc_rfc1001_len(&rsp->hdr, (rsp->hdr.WordCount * 2) + data_count); + + /* Send req->EchoCount - 1 number of ECHO response now & + * if SMB CANCEL for Echo comes don't send response + */ + for (i = 1; i < le16_to_cpu(req->EchoCount) && + !work->send_no_response; i++) { + rsp->SequenceNumber = cpu_to_le16(i); + ksmbd_conn_write(work); + } + + /* Last echo response */ + rsp->SequenceNumber = cpu_to_le16(i); + work->multiRsp = 0; + + return 0; +} + +/** + * smb_flush() - file sync - flush request handler + * @work: smb work containing flush command + * + * Return: 0 on success, otherwise error + */ +int smb_flush(struct ksmbd_work *work) +{ + struct smb_com_flush_req *req = work->request_buf; + struct smb_com_flush_rsp *rsp = work->response_buf; + int err = 0; + + ksmbd_debug(SMB, "SMB_COM_FLUSH called for fid %u\n", req->FileID); + + if (req->FileID == 0xFFFF) { + err = ksmbd_file_table_flush(work); + if (err) + goto out; + } else { + err = ksmbd_vfs_fsync(work, req->FileID, KSMBD_NO_FID); + if (err) + goto out; + } + + /* file fsync success, return response to server */ + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 0; + rsp->ByteCount = 0; + return err; + +out: + if (err) + rsp->hdr.Status.CifsError = STATUS_INVALID_HANDLE; + + return err; +} + +/***************************************************************************** + * TRANS2 command implementation functions + *****************************************************************************/ + +/** + * get_filetype() - convert file mode to smb file type + * @mode: file mode to be convertd + * + * Return: converted file type + */ +static __u32 get_filetype(mode_t mode) +{ + if (S_ISREG(mode)) + return UNIX_FILE; + else if (S_ISDIR(mode)) + return UNIX_DIR; + else if (S_ISLNK(mode)) + return UNIX_SYMLINK; + else if (S_ISCHR(mode)) + return UNIX_CHARDEV; + else if (S_ISBLK(mode)) + return UNIX_BLOCKDEV; + else if (S_ISFIFO(mode)) + return UNIX_FIFO; + else if (S_ISSOCK(mode)) + return UNIX_SOCKET; + + return UNIX_UNKNOWN; +} + +/** + * init_unix_info() - convert file stat information to smb file info format + * @unix_info: smb file information format + * @stat: unix file/dir stat information + */ +static void init_unix_info(struct file_unix_basic_info *unix_info, + struct user_namespace *user_ns, struct kstat *stat) +{ + u64 time; + + unix_info->EndOfFile = cpu_to_le64(stat->size); + unix_info->NumOfBytes = cpu_to_le64(512 * stat->blocks); + time = ksmbd_UnixTimeToNT(stat->ctime); + unix_info->LastStatusChange = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat->atime); + unix_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat->mtime); + unix_info->LastModificationTime = cpu_to_le64(time); + unix_info->Uid = cpu_to_le64(from_kuid(user_ns, stat->uid)); + unix_info->Gid = cpu_to_le64(from_kgid(user_ns, stat->gid)); + unix_info->Type = cpu_to_le32(get_filetype(stat->mode)); + unix_info->DevMajor = cpu_to_le64(MAJOR(stat->rdev)); + unix_info->DevMinor = cpu_to_le64(MINOR(stat->rdev)); + unix_info->UniqueId = cpu_to_le64(stat->ino); + unix_info->Permissions = cpu_to_le64(stat->mode); + unix_info->Nlinks = cpu_to_le64(stat->nlink); +} + +/** + * unix_info_to_attr() - convert smb file info format to unix attr format + * @unix_info: smb file information format + * @attrs: unix file/dir stat information + * + * Return: 0 + */ +static int unix_info_to_attr(struct file_unix_basic_info *unix_info, + struct user_namespace *user_ns, + struct iattr *attrs) +{ + struct timespec64 ts; + + if (le64_to_cpu(unix_info->EndOfFile) != NO_CHANGE_64) { + attrs->ia_size = le64_to_cpu(unix_info->EndOfFile); + attrs->ia_valid |= ATTR_SIZE; + } + + if (le64_to_cpu(unix_info->LastStatusChange) != NO_CHANGE_64) { + ts = smb_NTtimeToUnix(unix_info->LastStatusChange); + attrs->ia_ctime = ts; + attrs->ia_valid |= ATTR_CTIME; + } + + if (le64_to_cpu(unix_info->LastAccessTime) != NO_CHANGE_64) { + ts = smb_NTtimeToUnix(unix_info->LastAccessTime); + attrs->ia_atime = ts; + attrs->ia_valid |= ATTR_ATIME; + } + + if (le64_to_cpu(unix_info->LastModificationTime) != NO_CHANGE_64) { + ts = smb_NTtimeToUnix(unix_info->LastModificationTime); + attrs->ia_mtime = ts; + attrs->ia_valid |= ATTR_MTIME; + } + + if (le64_to_cpu(unix_info->Uid) != NO_CHANGE_64) { + attrs->ia_uid = make_kuid(user_ns, + le64_to_cpu(unix_info->Uid)); + attrs->ia_valid |= ATTR_UID; + } + + if (le64_to_cpu(unix_info->Gid) != NO_CHANGE_64) { + attrs->ia_gid = make_kgid(user_ns, + le64_to_cpu(unix_info->Gid)); + attrs->ia_valid |= ATTR_GID; + } + + if (le64_to_cpu(unix_info->Permissions) != NO_CHANGE_64) { + attrs->ia_mode = le64_to_cpu(unix_info->Permissions); + attrs->ia_valid |= ATTR_MODE; + } + + switch (le32_to_cpu(unix_info->Type)) { + case UNIX_FILE: + attrs->ia_mode |= S_IFREG; + break; + case UNIX_DIR: + attrs->ia_mode |= S_IFDIR; + break; + case UNIX_SYMLINK: + attrs->ia_mode |= S_IFLNK; + break; + case UNIX_CHARDEV: + attrs->ia_mode |= S_IFCHR; + break; + case UNIX_BLOCKDEV: + attrs->ia_mode |= S_IFBLK; + break; + case UNIX_FIFO: + attrs->ia_mode |= S_IFIFO; + break; + case UNIX_SOCKET: + attrs->ia_mode |= S_IFSOCK; + break; + default: + pr_err("unknown file type 0x%x\n", + le32_to_cpu(unix_info->Type)); + } + + return 0; +} + +/** + * unix_to_dos_time() - convert unix time to dos format + * @ts: unix style time + * @time: store dos style time + * @date: store dos style date + */ +static void unix_to_dos_time(struct timespec64 ts, __le16 *time, __le16 *date) +{ + struct tm t; + __u16 val; + + time64_to_tm(ts.tv_sec, (-sys_tz.tz_minuteswest) * 60, &t); + val = (((unsigned int)(t.tm_mon + 1)) >> 3) | ((t.tm_year - 80) << 1); + val = ((val & 0xFF) << 8) | (t.tm_mday | + (((t.tm_mon + 1) & 0x7) << 5)); + *date = cpu_to_le16(val); + + val = ((((unsigned int)t.tm_min >> 3) & 0x7) | + (((unsigned int)t.tm_hour) << 3)); + val = ((val & 0xFF) << 8) | ((t.tm_sec/2) | ((t.tm_min & 0x7) << 5)); + *time = cpu_to_le16(val); +} + +/** + * cifs_convert_ace() - helper function for convert an Access Control Entry + * from cifs wire format to local POSIX xattr format + * @ace: local - unix style Access Control Entry format + * @cifs_ace: cifs wire Access Control Entry format + */ +static void cifs_convert_ace(struct posix_acl_xattr_entry *ace, + struct cifs_posix_ace *cifs_ace) +{ + /* u8 cifs fields do not need le conversion */ + ace->e_perm = cpu_to_le16(cifs_ace->cifs_e_perm); + ace->e_tag = cpu_to_le16(cifs_ace->cifs_e_tag); + ace->e_id = cpu_to_le32(le64_to_cpu(cifs_ace->cifs_uid)); +} + +/** + * cifs_copy_posix_acl() - Convert ACL from CIFS POSIX wire format to local + * Linux POSIX ACL xattr + * @trgt: target buffer for storing in local ace format + * @src: source buffer in cifs ace format + * @buflen: target buffer length + * @acl_type: ace type + * @size_of_data_area: max buffer size to store ace xattr + * + * Return: size of convert ace xattr on success, otherwise error + */ +static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen, + const int acl_type, const int size_of_data_area) +{ + int size = 0; + int i; + __u16 count; + struct cifs_posix_ace *pACE; + struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)src; + struct posix_acl_xattr_entry *ace; + struct posix_acl_xattr_header *local_acl = (void *)trgt; + + if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION) + return -EOPNOTSUPP; + + if (acl_type & ACL_TYPE_ACCESS) { + count = le16_to_cpu(cifs_acl->access_entry_count); + pACE = &cifs_acl->ace_array[0]; + size = sizeof(struct cifs_posix_acl); + size += sizeof(struct cifs_posix_ace) * count; + /* check if we would go beyond end of SMB */ + if (size_of_data_area < size) { + ksmbd_debug(SMB, "bad CIFS POSIX ACL size %d vs. %d\n", + size_of_data_area, size); + return -EINVAL; + } + } else if (acl_type & ACL_TYPE_DEFAULT) { + count = le16_to_cpu(cifs_acl->default_entry_count); + pACE = &cifs_acl->ace_array[0]; + size = sizeof(struct cifs_posix_acl); + size += sizeof(struct cifs_posix_ace) * count; + /* check if we would go beyond end of SMB */ + if (size_of_data_area < size) + return -EINVAL; + } else { + /* illegal type */ + return -EINVAL; + } + + size = posix_acl_xattr_size(count); + if ((buflen != 0) && local_acl && size > buflen) + return -ERANGE; + + /* buffer big enough */ + ace = (void *)(local_acl + 1); + local_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION); + for (i = 0; i < count; i++) { + cifs_convert_ace(&ace[i], pACE); + pACE++; + } + + return size; +} + +/** + * convert_ace_to_cifs_ace() - helper function to convert ACL from local + * Linux POSIX ACL xattr to CIFS POSIX wire format to local + * @cifs_ace: target buffer for storing in cifs ace format + * @local_ace: source buffer in Linux POSIX ACL xattr format + * + * Return: 0 + */ +static __u16 convert_ace_to_cifs_ace(struct cifs_posix_ace *cifs_ace, + const struct posix_acl_xattr_entry *local_ace) +{ + __u16 rc = 0; /* 0 = ACL converted ok */ + + cifs_ace->cifs_e_perm = le16_to_cpu(local_ace->e_perm); + cifs_ace->cifs_e_tag = le16_to_cpu(local_ace->e_tag); + /* BB is there a better way to handle the large uid? */ + if (local_ace->e_id == cpu_to_le32(-1)) { + /* Probably no need to le convert -1 on any + * arch but can not hurt + */ + cifs_ace->cifs_uid = cpu_to_le64(-1); + } else + cifs_ace->cifs_uid = cpu_to_le64(le32_to_cpu(local_ace->e_id)); + return rc; +} + +/** + * ACL_to_cifs_posix() - ACL from local Linux POSIX xattr to CIFS POSIX ACL + * wire format + * @parm_data: target buffer for storing in cifs ace format + * @pACL: source buffer in cifs ace format + * @buflen: target buffer length + * @acl_type: ace type + * + * Return: 0 on success, otherwise error + */ +static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL, + const int buflen, const int acl_type) +{ + __u16 rc = 0; + struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)parm_data; + struct posix_acl_xattr_header *local_acl = (void *)pACL; + struct posix_acl_xattr_entry *ace = (void *)(local_acl + 1); + int count; + int i, j = 0; + + if ((buflen == 0) || !pACL || !cifs_acl) + return 0; + + count = posix_acl_xattr_count((size_t)buflen); + ksmbd_debug(SMB, "setting acl with %d entries from buf of length %d and version of %d\n", + count, buflen, le32_to_cpu(local_acl->a_version)); + if (le32_to_cpu(local_acl->a_version) != 2) { + ksmbd_debug(SMB, "unknown POSIX ACL version %d\n", + le32_to_cpu(local_acl->a_version)); + return 0; + } + if (acl_type == ACL_TYPE_ACCESS) { + cifs_acl->access_entry_count = cpu_to_le16(count); + j = 0; + } else if (acl_type == ACL_TYPE_DEFAULT) { + cifs_acl->default_entry_count = cpu_to_le16(count); + if (cifs_acl->access_entry_count) + j = le16_to_cpu(cifs_acl->access_entry_count); + } else { + ksmbd_debug(SMB, "unknown ACL type %d\n", acl_type); + return 0; + } + for (i = 0; i < count; i++, j++) { + rc = convert_ace_to_cifs_ace(&cifs_acl->ace_array[i], &ace[i]); + if (rc != 0) { + /* ACE not converted */ + break; + } + } + if (rc == 0) { + rc = (__u16)(count * sizeof(struct cifs_posix_ace)); + /* BB add check to make sure ACL does not overflow SMB */ + } + return rc; +} + +/** + * smb_get_acl() - handler for query posix acl information + * @work: smb work containing posix acl query command + * @path: path of file/dir to query acl + * + * Return: 0 on success, otherwise error + */ +static int smb_get_acl(struct ksmbd_work *work, struct path *path) +{ + struct smb_com_trans2_rsp *rsp = work->response_buf; + char *buf = NULL; + int rc = 0, value_len; + struct cifs_posix_acl *aclbuf; + __u16 rsp_data_cnt = 0; + + aclbuf = (struct cifs_posix_acl *)(work->response_buf + + sizeof(struct smb_com_trans2_rsp) + 4); + + aclbuf->version = cpu_to_le16(CIFS_ACL_VERSION); + aclbuf->default_entry_count = 0; + aclbuf->access_entry_count = 0; + + /* check if POSIX_ACL_XATTR_ACCESS exists */ + value_len = ksmbd_vfs_getxattr(mnt_user_ns(path->mnt), path->dentry, + XATTR_NAME_POSIX_ACL_ACCESS, + &buf); + if (value_len > 0) { + rsp_data_cnt += ACL_to_cifs_posix((char *)aclbuf, buf, + value_len, ACL_TYPE_ACCESS); + kfree(buf); + buf = NULL; + } + + /* check if POSIX_ACL_XATTR_DEFAULT exists */ + value_len = ksmbd_vfs_getxattr(mnt_user_ns(path->mnt), path->dentry, + XATTR_NAME_POSIX_ACL_DEFAULT, + &buf); + if (value_len > 0) { + rsp_data_cnt += ACL_to_cifs_posix((char *)aclbuf, buf, + value_len, ACL_TYPE_DEFAULT); + kfree(buf); + buf = NULL; + } + + if (rsp_data_cnt) + rsp_data_cnt += sizeof(struct cifs_posix_acl); + + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = cpu_to_le16(rsp_data_cnt); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + rsp->ByteCount = cpu_to_le16(rsp_data_cnt + 5); + inc_rfc1001_len(&rsp->hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + + if (buf) + kfree(buf); + return rc; +} + +/** + * smb_set_acl() - handler for setting posix acl information + * @work: smb work containing posix acl set command + * + * Return: 0 on success, otherwise error + */ +static int smb_set_acl(struct ksmbd_work *work) +{ + struct smb_com_trans2_spi_req *req = work->request_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct cifs_posix_acl *wire_acl_data; + char *fname, *buf = NULL; + int rc = 0, acl_type = 0, value_len; + + fname = smb_get_name(share, req->FileName, PATH_MAX, work, false); + if (IS_ERR(fname)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(fname); + } + + buf = vmalloc(XATTR_SIZE_MAX); + if (!buf) { + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + rc = -ENOMEM; + goto out; + } + + wire_acl_data = (struct cifs_posix_acl *)(((char *) &req->hdr.Protocol) + + le16_to_cpu(req->DataOffset)); + if (le16_to_cpu(wire_acl_data->access_entry_count) > 0 && + le16_to_cpu(wire_acl_data->access_entry_count) < 0xFFFF) { + acl_type = ACL_TYPE_ACCESS; + + } else if (le16_to_cpu(wire_acl_data->default_entry_count) > 0 && + le16_to_cpu(wire_acl_data->default_entry_count) < 0xFFFF) { + acl_type = ACL_TYPE_DEFAULT; + } else { + rc = -EINVAL; + goto out; + } + + rc = cifs_copy_posix_acl(buf, + (char *)wire_acl_data, + XATTR_SIZE_MAX, acl_type, XATTR_SIZE_MAX); + if (rc < 0) { + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + goto out; + } + + value_len = rc; + if (acl_type == ACL_TYPE_ACCESS) { + rc = ksmbd_vfs_fsetxattr(work, + fname, + XATTR_NAME_POSIX_ACL_ACCESS, + buf, value_len, 0); + } else if (acl_type == ACL_TYPE_DEFAULT) { + rc = ksmbd_vfs_fsetxattr(work, + fname, + XATTR_NAME_POSIX_ACL_DEFAULT, + buf, value_len, 0); + } + + if (rc < 0) { + rsp->hdr.Status.CifsError = STATUS_UNEXPECTED_IO_ERROR; + goto out; + } + + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = cpu_to_le16(0); + rsp->t2.ParameterCount = rsp->t2.TotalParameterCount; + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = cpu_to_le16(0); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + + /* 2 for parameter count + 1 pad1*/ + rsp->ByteCount = cpu_to_le16(3); + rsp->Pad = 0; + inc_rfc1001_len(&rsp->hdr, rsp->hdr.WordCount * 2 + 3); + +out: + if (buf) + vfree(buf); + kfree(fname); + return rc; +} + +static void *ksmbd_realloc_response(void *ptr, size_t old_sz, size_t new_sz) +{ + size_t sz = min(old_sz, new_sz); + void *nptr; + + nptr = kvmalloc(new_sz, GFP_KERNEL | __GFP_ZERO); + if (!nptr) + return ptr; + memcpy(nptr, ptr, sz); + kvfree(ptr); + return nptr; +} + +/** + * smb_readlink() - handler for reading symlink source path + * @work: smb work containing query link information + * + * Return: 0 on success, otherwise error + */ +static int smb_readlink(struct ksmbd_work *work, struct path *path) +{ + struct smb_com_trans2_qpi_req *req = work->request_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + int err, name_len; + char *buf, *ptr; + + buf = kzalloc((CIFS_MF_SYMLINK_LINK_MAXLEN), GFP_KERNEL); + if (!buf) { + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + return -ENOMEM; + } + + err = ksmbd_vfs_readlink(path, buf, CIFS_MF_SYMLINK_LINK_MAXLEN); + if (err < 0) { + rsp->hdr.Status.CifsError = STATUS_INVALID_HANDLE; + goto out; + } + + /* + * check if this namelen(unicode) and smb header can fit in small rsp + * buf. If not, switch to large rsp buffer. + */ + err++; + err *= 2; + if (err + MAX_HEADER_SIZE(work->conn) > work->response_sz) { + void *nptr; + size_t nsz = err + MAX_HEADER_SIZE(work->conn); + + nptr = ksmbd_realloc_response(work->response_buf, + work->response_sz, + nsz); + if (nptr == work->response_buf) { + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + err = -ENOMEM; + goto out; + } + + work->response_buf = nptr; + rsp = (struct smb_com_trans2_rsp *)work->response_buf; + } + err = 0; + + ptr = (char *)&rsp->Buffer[0]; + memset(ptr, 0, 4); + ptr += 4; + + if (is_smbreq_unicode(&req->hdr)) { + name_len = smb_strtoUTF16((__le16 *)ptr, + buf, + CIFS_MF_SYMLINK_LINK_MAXLEN, + work->conn->local_nls); + name_len++; /* trailing null */ + name_len *= 2; + } else { /* BB add path length overrun check */ + name_len = strscpy(ptr, buf, CIFS_MF_SYMLINK_LINK_MAXLEN - 1); + name_len++; /* trailing null */ + } + + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = cpu_to_le16(name_len); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + rsp->ByteCount = cpu_to_le16(name_len + 5); + inc_rfc1001_len(&rsp->hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + +out: + kfree(buf); + return err; +} + +/** + * smb_get_ea() - handler for extended attribute query + * @work: smb work containing query xattr command + * @path: path of file/dir to query xattr command + * + * Return: 0 on success, otherwise error + */ +static int smb_get_ea(struct ksmbd_work *work, struct path *path) +{ + struct smb_com_trans2_rsp *rsp = work->response_buf; + char *name, *ptr, *xattr_list = NULL, *buf; + int rc, name_len, value_len, xattr_list_len; + struct fealist *eabuf = (struct fealist *)(work->response_buf + + sizeof(struct smb_com_trans2_rsp) + 4); + struct fea *temp_fea; + ssize_t buf_free_len; + __u16 rsp_data_cnt = 4; + + eabuf->list_len = cpu_to_le32(rsp_data_cnt); + buf_free_len = work->response_sz - (get_rfc1002_len(rsp) + 4) - + sizeof(struct smb_com_trans2_rsp); + rc = ksmbd_vfs_listxattr(path->dentry, &xattr_list); + if (rc < 0) { + rsp->hdr.Status.CifsError = STATUS_INVALID_HANDLE; + goto out; + } else if (!rc) { /* there is no EA in the file */ + eabuf->list_len = cpu_to_le32(rsp_data_cnt); + goto done; + } + + xattr_list_len = rc; + rc = 0; + + ptr = (char *)eabuf->list; + temp_fea = (struct fea *)ptr; + for (name = xattr_list; name - xattr_list < xattr_list_len; + name += strlen(name) + 1) { + ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); + /* + * CIFS does not support EA other name user.* namespace, + * still keep the framework generic, to list other attrs + * in future. + */ + if (strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) + continue; + + name_len = strlen(name); + if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) + name_len -= XATTR_USER_PREFIX_LEN; + + ptr = (char *)(&temp_fea->name + name_len + 1); + buf_free_len -= (offsetof(struct fea, name) + name_len + 1); + + value_len = ksmbd_vfs_getxattr(mnt_user_ns(path->mnt), + path->dentry, name, &buf); + if (value_len <= 0) { + rc = -ENOENT; + rsp->hdr.Status.CifsError = STATUS_INVALID_HANDLE; + goto out; + } + + memcpy(ptr, buf, value_len); + kfree(buf); + + temp_fea->EA_flags = 0; + temp_fea->name_len = name_len; + if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) + memcpy(temp_fea->name, &name[XATTR_USER_PREFIX_LEN], + name_len); + else + memcpy(temp_fea->name, name, name_len); + + temp_fea->value_len = cpu_to_le16(value_len); + buf_free_len -= value_len; + rsp_data_cnt += offsetof(struct fea, name) + name_len + 1 + + value_len; + eabuf->list_len += cpu_to_le32(offsetof(struct fea, name) + + name_len + 1 + value_len); + ptr += value_len; + temp_fea = (struct fea *)ptr; + } + +done: + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = cpu_to_le16(rsp_data_cnt); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + rsp->ByteCount = cpu_to_le16(rsp_data_cnt + 5); + inc_rfc1001_len(&rsp->hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); +out: + kvfree(xattr_list); + return rc; +} + +/** + * query_path_info() - handler for query path info + * @work: smb work containing query path info command + * + * Return: 0 on success, otherwise error + */ +static int query_path_info(struct ksmbd_work *work) +{ + struct smb_hdr *rsp_hdr = work->response_buf; + struct smb_com_trans2_req *req = work->request_buf; + struct ksmbd_conn *conn = work->conn; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + struct trans2_qpi_req_params *req_params; + char *name = NULL; + struct path path; + struct kstat st; + int rc; + char *ptr; + __u64 create_time = 0, time; + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) { + rsp_hdr->Status.CifsError = STATUS_UNEXPECTED_IO_ERROR; + return 0; + } + + req_params = (struct trans2_qpi_req_params *)(work->request_buf + + le16_to_cpu(req->ParameterOffset) + 4); + name = smb_get_name(share, req_params->FileName, PATH_MAX, work, + false); + if (IS_ERR(name)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(name); + } + + if (ksmbd_override_fsids(work)) { + kfree(name); + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + return -ENOMEM; + } + + rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, 0); + if (rc) { + if (rc == -EACCES || rc == -EXDEV) + rsp_hdr->Status.CifsError = STATUS_ACCESS_DENIED; + else + rsp_hdr->Status.CifsError = + STATUS_OBJECT_NAME_NOT_FOUND; + ksmbd_debug(SMB, "cannot get linux path for %s, err %d\n", + name, rc); + goto out; + } + + if (d_is_symlink(path.dentry)) { + rsp_hdr->Status.CifsError = STATUS_ACCESS_DENIED; + goto err_out; + } + + rc = vfs_getattr(&path, &st, STATX_BASIC_STATS, AT_STATX_SYNC_AS_STAT); + if (rc) { + pr_err("cannot get stat information\n"); + goto err_out; + } + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + struct xattr_dos_attrib da; + + rc = ksmbd_vfs_get_dos_attrib_xattr(mnt_user_ns(path.mnt), + path.dentry, &da); + if (rc > 0) + create_time = da.create_time; + rc = 0; + } + + switch (le16_to_cpu(req_params->InformationLevel)) { + case SMB_INFO_STANDARD: + { + struct file_info_standard *infos; + + ksmbd_debug(SMB, "SMB_INFO_STANDARD\n"); + rc = ksmbd_query_inode_status(d_inode(path.dentry)); + if (rc == KSMBD_INODE_STATUS_PENDING_DELETE) { + rc = -EBUSY; + goto err_out; + } + + rc = 0; + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + infos = (struct file_info_standard *)(ptr + 4); + unix_to_dos_time(ksmbd_NTtimeToUnix(cpu_to_le64(create_time)), + &infos->CreationDate, &infos->CreationTime); + unix_to_dos_time(st.atime, + &infos->LastAccessDate, + &infos->LastAccessTime); + unix_to_dos_time(st.mtime, + &infos->LastWriteDate, + &infos->LastWriteTime); + infos->DataSize = cpu_to_le32(st.size); + infos->AllocationSize = cpu_to_le32(st.blocks << 9); + infos->Attributes = cpu_to_le16(S_ISDIR(st.mode) ? + ATTR_DIRECTORY : ATTR_ARCHIVE); + infos->EASize = 0; + + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = cpu_to_le16(22); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(22); + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + rsp->ByteCount = cpu_to_le16(27); + rsp->Pad = 0; + inc_rfc1001_len(rsp_hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + break; + } + case SMB_QUERY_FILE_STANDARD_INFO: + { + struct file_standard_info *standard_info; + unsigned int del_pending; + + ksmbd_debug(SMB, "SMB_QUERY_FILE_STANDARD_INFO\n"); + del_pending = ksmbd_query_inode_status(d_inode(path.dentry)); + if (del_pending == KSMBD_INODE_STATUS_PENDING_DELETE) + del_pending = 1; + else + del_pending = 0; + + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = + cpu_to_le16(sizeof(struct file_standard_info)); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = + cpu_to_le16(sizeof(struct file_standard_info)); + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + /*2 for parameter count & 3 pad (1pad1 + 2 pad2)*/ + rsp->ByteCount = + cpu_to_le16(2 + sizeof(struct file_standard_info) + 3); + rsp->Pad = 0; + /* lets set EA info */ + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + standard_info = (struct file_standard_info *)(ptr + 4); + standard_info->AllocationSize = cpu_to_le64(st.blocks << 9); + standard_info->EndOfFile = cpu_to_le64(st.size); + standard_info->NumberOfLinks = cpu_to_le32(get_nlink(&st) - + del_pending); + standard_info->DeletePending = del_pending; + standard_info->Directory = S_ISDIR(st.mode) ? 1 : 0; + inc_rfc1001_len(rsp_hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + break; + } + case SMB_QUERY_FILE_BASIC_INFO: + { + struct file_basic_info *basic_info; + + ksmbd_debug(SMB, "SMB_QUERY_FILE_BASIC_INFO\n"); + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = + cpu_to_le16(sizeof(struct file_basic_info)); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(sizeof(struct file_basic_info)); + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + /*2 for parameter count & 3 pad (1pad1 + 2 pad2)*/ + rsp->ByteCount = + cpu_to_le16(2 + sizeof(struct file_basic_info) + 3); + rsp->Pad = 0; + /* lets set EA info */ + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + basic_info = (struct file_basic_info *)(ptr + 4); + basic_info->CreationTime = cpu_to_le64(create_time); + time = ksmbd_UnixTimeToNT(st.atime); + basic_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(st.mtime); + basic_info->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(st.ctime); + basic_info->ChangeTime = cpu_to_le64(time); + basic_info->Attributes = S_ISDIR(st.mode) ? + ATTR_DIRECTORY_LE : ATTR_ARCHIVE_LE; + basic_info->Pad = 0; + inc_rfc1001_len(rsp_hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + break; + } + case SMB_QUERY_FILE_EA_INFO: + { + struct file_ea_info *ea_info; + + ksmbd_debug(SMB, "SMB_QUERY_FILE_EA_INFO\n"); + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = + cpu_to_le16(sizeof(struct file_ea_info)); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(sizeof(struct file_ea_info)); + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + /*2 for parameter count & 3 pad (1pad1 + 2 pad2)*/ + rsp->ByteCount = + cpu_to_le16(2 + sizeof(struct file_ea_info) + 3); + rsp->Pad = 0; + /* lets set EA info */ + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + ea_info = (struct file_ea_info *)(ptr + 4); + ea_info->EaSize = 0; + inc_rfc1001_len(rsp_hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + break; + } + case SMB_QUERY_FILE_NAME_INFO: + { + struct file_name_info *name_info; + int uni_filename_len; + char *filename; + + ksmbd_debug(SMB, "SMB_QUERY_FILE_NAME_INFO\n"); + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + name_info = (struct file_name_info *)(ptr + 4); + + filename = convert_to_nt_pathname(work->tcon->share_conf, &path); + if (!filename) { + rc = -ENOMEM; + goto err_out; + } + uni_filename_len = smbConvertToUTF16( + (__le16 *)name_info->FileName, + filename, PATH_MAX, + conn->local_nls, 0); + kfree(filename); + uni_filename_len *= 2; + name_info->FileNameLength = cpu_to_le32(uni_filename_len); + + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = cpu_to_le16(uni_filename_len + 4); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(uni_filename_len + 4); + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + /*2 for parameter count & 3 pad (1pad1 + 2 pad2)*/ + rsp->ByteCount = cpu_to_le16(2 + uni_filename_len + 4 + 3); + rsp->Pad = 0; + inc_rfc1001_len(rsp_hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + break; + } + case SMB_QUERY_FILE_ALL_INFO: + { + struct file_all_info *ainfo; + unsigned int del_pending; + char *filename; + int uni_filename_len, total_count = 72; + + ksmbd_debug(SMB, "SMB_QUERY_FILE_ALL_INFO\n"); + + del_pending = ksmbd_query_inode_status(d_inode(path.dentry)); + if (del_pending == KSMBD_INODE_STATUS_PENDING_DELETE) + del_pending = 1; + else + del_pending = 0; + + filename = convert_to_nt_pathname(work->tcon->share_conf, &path); + if (!filename) { + rc = -ENOMEM; + goto err_out; + } + + /* + * Observation: sizeof smb_hdr is 33 bytes(including word count) + * After that: trans2 response 22 bytes when stepcount 0 and + * including ByteCount storage. + */ + /* lets set EA info */ + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + ainfo = (struct file_all_info *) (ptr + 4); + + ainfo->CreationTime = cpu_to_le64(create_time); + time = ksmbd_UnixTimeToNT(st.atime); + ainfo->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(st.mtime); + ainfo->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(st.ctime); + ainfo->ChangeTime = cpu_to_le64(time); + ainfo->Attributes = S_ISDIR(st.mode) ? + ATTR_DIRECTORY_LE : ATTR_ARCHIVE_LE; + ainfo->Pad1 = 0; + ainfo->AllocationSize = cpu_to_le64(st.blocks << 9); + ainfo->EndOfFile = cpu_to_le64(st.size); + ainfo->NumberOfLinks = cpu_to_le32(get_nlink(&st) - + del_pending); + ainfo->DeletePending = del_pending; + ainfo->Directory = S_ISDIR(st.mode) ? 1 : 0; + ainfo->Pad2 = 0; + ainfo->EASize = 0; + uni_filename_len = smbConvertToUTF16( + (__le16 *)ainfo->FileName, + filename, PATH_MAX, + conn->local_nls, 0); + kfree(filename); + uni_filename_len *= 2; + ainfo->FileNameLength = cpu_to_le32(uni_filename_len); + total_count += uni_filename_len; + + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + /* add unicode name length of name */ + rsp->t2.TotalDataCount = cpu_to_le16(total_count); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(total_count); + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + /* 2 for parameter count + 72 data count + + * filename length + 3 pad (1pad1 + 2 pad2) + */ + rsp->ByteCount = cpu_to_le16(5 + total_count); + rsp->Pad = 0; + inc_rfc1001_len(rsp_hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + break; + } + case SMB_QUERY_ALT_NAME_INFO: + { + struct alt_name_info *alt_name_info; + char *base; + int filename_len; + + ksmbd_debug(SMB, "SMB_QUERY_ALT_NAME_INFO\n"); + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + /*2 for parameter count & 3 pad (1pad1 + 2 pad2)*/ + rsp->ByteCount = cpu_to_le16(25); + rsp->Pad = 0; + /* lets set EA info */ + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + alt_name_info = (struct alt_name_info *)(ptr + 4); + + base = strrchr(name, '/'); + if (!base) + base = name; + else + base += 1; + + filename_len = ksmbd_extract_shortname(conn, base, + alt_name_info->FileName); + alt_name_info->FileNameLength = cpu_to_le32(filename_len); + rsp->t2.TotalDataCount = cpu_to_le16(4 + filename_len); + rsp->t2.DataCount = cpu_to_le16(4 + filename_len); + + inc_rfc1001_len(rsp_hdr, (4 + filename_len + 25)); + break; + } + case SMB_QUERY_FILE_UNIX_BASIC: + { + struct file_unix_basic_info *unix_info; + + ksmbd_debug(SMB, "SMB_QUERY_FILE_UNIX_BASIC\n"); + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = 0; + rsp->t2.TotalDataCount = cpu_to_le16(100); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = 0; + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(100); + rsp->t2.DataOffset = cpu_to_le16(56); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + rsp->ByteCount = cpu_to_le16(101); /* 100 data count + 1pad */ + rsp->Pad = 0; + unix_info = (struct file_unix_basic_info *)(&rsp->Pad + 1); + init_unix_info(unix_info, mnt_user_ns(path.mnt), &st); + inc_rfc1001_len(rsp_hdr, (10 * 2 + 101)); + break; + } + case SMB_QUERY_FILE_INTERNAL_INFO: + { + struct file_internal_info *iinfo; + + ksmbd_debug(SMB, "SMB_QUERY_FILE_INTERNAL_INFO\n"); + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = cpu_to_le16(8); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(8); + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + rsp->ByteCount = cpu_to_le16(13); + rsp->Pad = 0; + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + iinfo = (struct file_internal_info *) (ptr + 4); + iinfo->UniqueId = cpu_to_le64(st.ino); + inc_rfc1001_len(rsp_hdr, (10 * 2 + 13)); + break; + } + case SMB_QUERY_FILE_UNIX_LINK: + ksmbd_debug(SMB, "SMB_QUERY_FILE_UNIX_LINK\n"); + rc = smb_readlink(work, &path); + if (rc < 0) + goto err_out; + break; + case SMB_INFO_QUERY_ALL_EAS: + ksmbd_debug(SMB, "SMB_INFO_QUERY_ALL_EAS\n"); + rc = smb_get_ea(work, &path); + if (rc < 0) + goto err_out; + break; + case SMB_QUERY_POSIX_ACL: + ksmbd_debug(SMB, "SMB_QUERY_POSIX_ACL\n"); + rc = smb_get_acl(work, &path); + if (rc < 0) + goto err_out; + break; + default: + pr_err("query path info not implemnted for %x\n", + le16_to_cpu(req_params->InformationLevel)); + rc = -EINVAL; + goto err_out; + } + +err_out: + path_put(&path); +out: + ksmbd_revert_fsids(work); + kfree(name); + return rc; +} + +/** + * create_trans2_reply() - create response for trans2 request + * @work: smb work containing smb response buffer + * @count: trans2 response buffer size + */ +static void create_trans2_reply(struct ksmbd_work *work, __u16 count) +{ + struct smb_hdr *rsp_hdr = work->response_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + + rsp_hdr->WordCount = 0x0A; + rsp->t2.TotalParameterCount = 0; + rsp->t2.TotalDataCount = cpu_to_le16(count); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = 0; + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(count); + rsp->t2.DataOffset = cpu_to_le16(56); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + + rsp->ByteCount = cpu_to_le16(count + 1); + rsp->Pad = 0; + inc_rfc1001_len(rsp_hdr, 10 * 2 + (count + 1)); +} + +/** + * set_fs_info() - handler for set fs info commands + * @work: smb work containing set fs info command buffer + * + * Return: 0 on success, otherwise error + */ +static int set_fs_info(struct ksmbd_work *work) +{ + struct smb_com_trans2_setfsi_req *req = work->request_buf; + struct smb_com_trans2_setfsi_rsp *rsp = work->response_buf; + int info_level = le16_to_cpu(req->InformationLevel); + + switch (info_level) { + u64 client_cap; + + case SMB_SET_CIFS_UNIX_INFO: + ksmbd_debug(SMB, "SMB_SET_CIFS_UNIX_INFO\n"); + if (le16_to_cpu(req->ClientUnixMajor) != + CIFS_UNIX_MAJOR_VERSION) { + pr_err("Non compatible unix major info\n"); + return -EINVAL; + } + + if (le16_to_cpu(req->ClientUnixMinor) != + CIFS_UNIX_MINOR_VERSION) { + pr_err("Non compatible unix minor info\n"); + return -EINVAL; + } + + client_cap = le64_to_cpu(req->ClientUnixCap); + ksmbd_debug(SMB, "clients unix cap = %llx\n", client_cap); + /* TODO: process caps */ + rsp->t2.TotalDataCount = 0; + break; + default: + ksmbd_debug(SMB, "info level %x not supported\n", info_level); + return -EINVAL; + } + + create_trans2_reply(work, le16_to_cpu(rsp->t2.TotalDataCount)); + return 0; +} + +/** + * query_fs_info() - handler for query fs info commands + * @work: smb work containing query fs info command buffer + * + * Return: 0 on success, otherwise error + */ +static int query_fs_info(struct ksmbd_work *work) +{ + struct smb_hdr *req_hdr = work->request_buf; + struct smb_com_trans2_req *req = work->request_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + struct smb_com_trans2_qfsi_req_params *req_params; + struct ksmbd_conn *conn = work->conn; + struct kstatfs stfs; + struct ksmbd_share_config *share; + int rc; + struct path path; + bool incomplete = false; + int info_level, len = 0; + struct ksmbd_tree_connect *tree_conn; + + req_params = (struct smb_com_trans2_qfsi_req_params *) + (work->request_buf + le16_to_cpu(req->ParameterOffset) + 4); + /* check if more data is coming */ + if (le16_to_cpu(req->TotalParameterCount) != + le16_to_cpu(req->ParameterCount)) { + ksmbd_debug(SMB, "total param = %d, received = %d\n", + le16_to_cpu(req->TotalParameterCount), + le16_to_cpu(req->ParameterCount)); + incomplete = true; + } + + if (le16_to_cpu(req->TotalDataCount) != le16_to_cpu(req->DataCount)) { + ksmbd_debug(SMB, "total data = %d, received = %d\n", + le16_to_cpu(req->TotalDataCount), + le16_to_cpu(req->DataCount)); + incomplete = true; + } + + if (incomplete) { + /* create 1 trans_state structure + * and add to connection list + */ + } + + info_level = le16_to_cpu(req_params->InformationLevel); + + tree_conn = ksmbd_tree_conn_lookup(work->sess, + le16_to_cpu(req_hdr->Tid)); + if (!tree_conn) + return -ENOENT; + share = tree_conn->share_conf; + + if (test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) + return -ENOENT; + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + + rc = kern_path(share->path, LOOKUP_NO_SYMLINKS, &path); + if (rc) { + ksmbd_revert_fsids(work); + pr_err("cannot create vfs path\n"); + return rc; + } + + rc = vfs_statfs(&path, &stfs); + if (rc) { + pr_err("cannot do stat of path %s\n", share->path); + goto err_out; + } + + switch (info_level) { + case SMB_INFO_ALLOCATION: + { + struct filesystem_alloc_info *ainfo; + + ksmbd_debug(SMB, "GOT SMB_INFO_ALLOCATION\n"); + rsp->t2.TotalDataCount = cpu_to_le16(18); + ainfo = (struct filesystem_alloc_info *)(&rsp->Pad + 1); + ainfo->fsid = 0; + ainfo->BytesPerSector = cpu_to_le16(512); + ainfo->SectorsPerAllocationUnit = + cpu_to_le32(stfs.f_bsize/le16_to_cpu(ainfo->BytesPerSector)); + ainfo->TotalAllocationUnits = cpu_to_le32(stfs.f_blocks); + ainfo->FreeAllocationUnits = cpu_to_le32(stfs.f_bfree); + break; + } + case SMB_QUERY_FS_VOLUME_INFO: + { + struct filesystem_vol_info *vinfo; + + ksmbd_debug(SMB, "GOT SMB_QUERY_FS_VOLUME_INFO\n"); + vinfo = (struct filesystem_vol_info *)(&rsp->Pad + 1); + vinfo->VolumeCreationTime = 0; + /* Taking dummy value of serial number*/ + vinfo->SerialNumber = cpu_to_le32(0xbc3ac512); + len = smbConvertToUTF16((__le16 *)vinfo->VolumeLabel, + share->name, PATH_MAX, conn->local_nls, 0); + vinfo->VolumeLabelSize = cpu_to_le32(len); + vinfo->Reserved = 0; + rsp->t2.TotalDataCount = + cpu_to_le16(sizeof(struct filesystem_vol_info) + + len - 2); + break; + } + case SMB_QUERY_FS_SIZE_INFO: + { + struct filesystem_info *sinfo; + + ksmbd_debug(SMB, "GOT SMB_QUERY_FS_SIZE_INFO\n"); + rsp->t2.TotalDataCount = cpu_to_le16(24); + sinfo = (struct filesystem_info *)(&rsp->Pad + 1); + sinfo->BytesPerSector = cpu_to_le32(512); + sinfo->SectorsPerAllocationUnit = + cpu_to_le32(stfs.f_bsize/sinfo->BytesPerSector); + sinfo->TotalAllocationUnits = cpu_to_le64(stfs.f_blocks); + sinfo->FreeAllocationUnits = cpu_to_le64(stfs.f_bfree); + break; + } + case SMB_QUERY_FS_DEVICE_INFO: + { + struct filesystem_device_info *fdi; + + /* query fs info device info response is 0 word and 8 bytes */ + ksmbd_debug(SMB, "GOT SMB_QUERY_FS_DEVICE_INFO\n"); + if (le16_to_cpu(req->MaxDataCount) < 8) { + pr_err("Insufficient bytes, cannot response()\n"); + rc = -EINVAL; + goto err_out; + } + + rsp->t2.TotalDataCount = cpu_to_le16(18); + fdi = (struct filesystem_device_info *)(&rsp->Pad + 1); + fdi->DeviceType = cpu_to_le32(FILE_DEVICE_DISK); + fdi->DeviceCharacteristics = cpu_to_le32(0x20); + break; + } + case SMB_QUERY_FS_ATTRIBUTE_INFO: + { + struct filesystem_attribute_info *info; + + ksmbd_debug(SMB, "GOT SMB_QUERY_FS_ATTRIBUTE_INFO\n"); + /* constant 12 bytes + variable filesystem name */ + info = (struct filesystem_attribute_info *)(&rsp->Pad + 1); + + if (le16_to_cpu(req->MaxDataCount) < 12) { + pr_err("Insufficient bytes, cannot response()\n"); + rc = -EINVAL; + goto err_out; + } + + info->Attributes = cpu_to_le32(FILE_CASE_PRESERVED_NAMES | + FILE_CASE_SENSITIVE_SEARCH | + FILE_VOLUME_QUOTAS); + info->MaxPathNameComponentLength = cpu_to_le32(stfs.f_namelen); + info->FileSystemNameLen = 0; + rsp->t2.TotalDataCount = cpu_to_le16(12); + break; + } + case SMB_QUERY_CIFS_UNIX_INFO: + { + struct filesystem_unix_info *uinfo; + + ksmbd_debug(SMB, "GOT SMB_QUERY_CIFS_UNIX_INFO\n"); + /* constant 12 bytes + variable filesystem name */ + uinfo = (struct filesystem_unix_info *)(&rsp->Pad + 1); + + if (le16_to_cpu(req->MaxDataCount) < 12) { + pr_err("Insufficient bytes, cannot response()\n"); + rc = -EINVAL; + goto err_out; + } + uinfo->MajorVersionNumber = + cpu_to_le16(CIFS_UNIX_MAJOR_VERSION); + uinfo->MinorVersionNumber = + cpu_to_le16(CIFS_UNIX_MINOR_VERSION); + uinfo->Capability = cpu_to_le64(SMB_UNIX_CAPS); + rsp->t2.TotalDataCount = cpu_to_le16(12); + break; + } + case SMB_QUERY_POSIX_FS_INFO: + { + struct filesystem_posix_info *pinfo; + + ksmbd_debug(SMB, "GOT SMB_QUERY_POSIX_FS_INFO\n"); + rsp->t2.TotalDataCount = cpu_to_le16(56); + pinfo = (struct filesystem_posix_info *)(&rsp->Pad + 1); + pinfo->BlockSize = cpu_to_le32(stfs.f_bsize); + pinfo->OptimalTransferSize = cpu_to_le32(stfs.f_blocks); + pinfo->TotalBlocks = cpu_to_le64(stfs.f_blocks); + pinfo->BlocksAvail = cpu_to_le64(stfs.f_bfree); + pinfo->UserBlocksAvail = cpu_to_le64(stfs.f_bavail); + pinfo->TotalFileNodes = cpu_to_le64(stfs.f_files); + pinfo->FreeFileNodes = cpu_to_le64(stfs.f_ffree); + pinfo->FileSysIdentifier = 0; + break; + } + default: + ksmbd_debug(SMB, "info level %x not implemented\n", info_level); + rc = -EINVAL; + goto err_out; + } + + create_trans2_reply(work, le16_to_cpu(rsp->t2.TotalDataCount)); + +err_out: + path_put(&path); + ksmbd_revert_fsids(work); + return rc; +} + +/** + * smb_posix_convert_flags() - convert smb posix access flags to open flags + * @flags: smb posix access flags + * + * Return: file open flags + */ +static __u32 smb_posix_convert_flags(__u32 flags, int *may_flags) +{ + __u32 posix_flags = 0; + + if ((flags & SMB_ACCMODE) == SMB_O_RDONLY) + posix_flags = O_RDONLY; + else if ((flags & SMB_ACCMODE) == SMB_O_WRONLY) + posix_flags = O_WRONLY; + else if ((flags & SMB_ACCMODE) == SMB_O_RDWR) + posix_flags = O_RDWR; + + if (flags & SMB_O_SYNC) + posix_flags |= O_DSYNC; + if (flags & SMB_O_DIRECTORY) + posix_flags |= O_DIRECTORY; + if (flags & SMB_O_NOFOLLOW) + posix_flags |= O_NOFOLLOW; + if (flags & SMB_O_APPEND) + posix_flags |= O_APPEND; + + *may_flags = ksmbd_openflags_to_mayflags(posix_flags); + + return posix_flags; +} + +/** + * smb_get_disposition() - convert smb disposition flags to open flags + * @flags: smb file disposition flags + * @file_present: file already present or not + * @stat: file stat information + * @open_flags: open flags should be stored here + * + * Return: file disposition flags + */ +static int smb_get_disposition(unsigned int flags, bool file_present, + struct kstat *stat, unsigned int *open_flags) +{ + int dispostion, disp_flags; + + if ((flags & (SMB_O_CREAT | SMB_O_EXCL)) == (SMB_O_CREAT | SMB_O_EXCL)) + dispostion = FILE_CREATE; + else if ((flags & (SMB_O_CREAT | SMB_O_TRUNC)) == + (SMB_O_CREAT | SMB_O_TRUNC)) + dispostion = FILE_OVERWRITE_IF; + else if ((flags & SMB_O_CREAT) == SMB_O_CREAT) + dispostion = FILE_OPEN_IF; + else if ((flags & SMB_O_TRUNC) == SMB_O_TRUNC) + dispostion = FILE_OVERWRITE; + else if ((flags & (SMB_O_CREAT | SMB_O_EXCL | SMB_O_TRUNC)) == 0) + dispostion = FILE_OPEN; + else + dispostion = FILE_SUPERSEDE; + + disp_flags = file_create_dispostion_flags(dispostion, file_present); + if (disp_flags < 0) + return disp_flags; + + *open_flags |= disp_flags; + return disp_flags; +} + +/** + * smb_posix_open() - handler for smb posix open + * @work: smb work containing posix open command + * + * Return: 0 on success, otherwise error + */ +static int smb_posix_open(struct ksmbd_work *work) +{ + struct smb_com_trans2_spi_req *pSMB_req = work->request_buf; + struct smb_com_trans2_spi_rsp *pSMB_rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct open_psx_req *psx_req; + struct open_psx_rsp *psx_rsp; + struct file_unix_basic_info *unix_info; + struct path path; + struct kstat stat; + __u16 data_offset, rsp_info_level, file_info = 0; + __u32 oplock_flags, posix_open_flags, may_flags; + umode_t mode; + char *name; + bool file_present = true; + int err; + struct ksmbd_file *fp = NULL; + int oplock_rsp = OPLOCK_NONE; + + name = smb_get_name(share, pSMB_req->FileName, PATH_MAX, work, false); + if (IS_ERR(name)) { + pSMB_rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(name); + } + + if (ksmbd_override_fsids(work)) { + pSMB_rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + kfree(name); + return -ENOMEM; + } + + err = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, 0); + if (err) { + file_present = false; + ksmbd_debug(SMB, "cannot get linux path for %s, err = %d\n", + name, err); + if (err == -EACCES || err == -EXDEV) + goto out; + } else { + if (d_is_symlink(path.dentry)) { + err = -EACCES; + goto free_path; + } + err = vfs_getattr(&path, &stat, STATX_BASIC_STATS, + AT_STATX_SYNC_AS_STAT); + if (err) { + pr_err("can not stat %s, err = %d\n", name, err); + goto free_path; + } + } + + data_offset = le16_to_cpu(pSMB_req->DataOffset); + psx_req = (struct open_psx_req *)(((char *)&pSMB_req->hdr.Protocol) + + data_offset); + oplock_flags = le32_to_cpu(psx_req->OpenFlags); + + posix_open_flags = smb_posix_convert_flags( + le32_to_cpu(psx_req->PosixOpenFlags), + &may_flags); + err = smb_get_disposition(le32_to_cpu(psx_req->PosixOpenFlags), + file_present, &stat, + &posix_open_flags); + if (err < 0) { + ksmbd_debug(SMB, "create_dispostion returned %d\n", err); + if (file_present) + goto free_path; + else + goto out; + } + + ksmbd_debug(SMB, "filename : %s, posix_open_flags : %x\n", name, + posix_open_flags); + mode = (umode_t) le64_to_cpu(psx_req->Permissions); + rsp_info_level = le16_to_cpu(psx_req->Level); + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + if (posix_open_flags & O_CREAT) { + err = -EACCES; + ksmbd_debug(SMB, + "returning as user does not have permission to write\n"); + if (file_present) + goto free_path; + else + goto out; + } + } + + /* posix mkdir command */ + if (posix_open_flags == (O_DIRECTORY | O_CREAT)) { + if (file_present) { + err = -EEXIST; + goto free_path; + } + + err = ksmbd_vfs_mkdir(work, name, mode); + if (err) + goto out; + + err = ksmbd_vfs_kern_path(work, name, 0, &path, 0); + if (err) { + pr_err("cannot get linux path, err = %d\n", err); + goto out; + } + ksmbd_debug(SMB, "mkdir done for %s, inode %lu\n", + name, d_inode(path.dentry)->i_ino); + goto prepare_rsp; + } + + if (!file_present && (posix_open_flags & O_CREAT)) { + err = ksmbd_vfs_create(work, name, mode); + if (err) + goto out; + + err = ksmbd_vfs_kern_path(work, name, 0, &path, 0); + if (err) { + pr_err("cannot get linux path, err = %d\n", err); + goto out; + } + } else if (file_present) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + err = inode_permission(mnt_user_ns(path.mnt), + d_inode(path.dentry), + may_flags); +#else + err = inode_permission(d_inode(path.dentry), + may_flags); +#endif + if (err) + goto free_path; + } + + fp = ksmbd_vfs_dentry_open(work, &path, posix_open_flags, + 0, file_present); + if (IS_ERR(fp)) { + err = PTR_ERR(fp); + fp = NULL; + goto free_path; + } + fp->pid = le16_to_cpu(pSMB_req->hdr.Pid); + + write_lock(&fp->f_ci->m_lock); + list_add(&fp->node, &fp->f_ci->m_fp_list); + write_unlock(&fp->f_ci->m_lock); + + if (smb1_oplock_enable && + test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_OPLOCKS) && + !S_ISDIR(file_inode(fp->filp)->i_mode)) { + /* Client cannot request levelII oplock directly */ + err = smb_grant_oplock(work, oplock_flags & + (REQ_OPLOCK | REQ_BATCHOPLOCK), fp->volatile_id, fp, + le16_to_cpu(pSMB_req->hdr.Tid), NULL, 0); + if (err) + goto free_path; + } + + oplock_rsp = fp->f_opinfo != NULL ? fp->f_opinfo->level : 0; + +prepare_rsp: + /* open/mkdir success, send back response */ + data_offset = sizeof(struct smb_com_trans2_spi_rsp) - + sizeof(pSMB_rsp->hdr.smb_buf_length) + + 3 /*alignment*/; + psx_rsp = (struct open_psx_rsp *)(((char *)&pSMB_rsp->hdr.Protocol) + + data_offset); + if (data_offset + sizeof(struct open_psx_rsp) > work->response_sz) { + err = -EIO; + goto free_path; + } + + psx_rsp->OplockFlags = cpu_to_le16(oplock_rsp); + psx_rsp->Fid = fp != NULL ? fp->volatile_id : 0; + + if (file_present) { + if (!(posix_open_flags & O_TRUNC)) + file_info = F_OPENED; + else + file_info = F_OVERWRITTEN; + } else + file_info = F_CREATED; + psx_rsp->CreateAction = cpu_to_le32(file_info); + + if (rsp_info_level != SMB_QUERY_FILE_UNIX_BASIC) { + ksmbd_debug(SMB, "returning null information level response"); + rsp_info_level = SMB_NO_INFO_LEVEL_RESPONSE; + } + psx_rsp->ReturnedLevel = cpu_to_le16(rsp_info_level); + + err = vfs_getattr(&path, &stat, STATX_BASIC_STATS, + AT_STATX_SYNC_AS_STAT); + if (err) { + pr_err("cannot get stat information\n"); + goto free_path; + } + + pSMB_rsp->hdr.Status.CifsError = STATUS_SUCCESS; + unix_info = (struct file_unix_basic_info *)((char *)psx_rsp + + sizeof(struct open_psx_rsp)); + init_unix_info(unix_info, mnt_user_ns(path.mnt), &stat); + + pSMB_rsp->hdr.WordCount = 10; + pSMB_rsp->t2.TotalParameterCount = cpu_to_le16(2); + pSMB_rsp->t2.TotalDataCount = cpu_to_le16(sizeof(struct open_psx_rsp) + + sizeof(struct file_unix_basic_info)); + pSMB_rsp->t2.ParameterCount = pSMB_rsp->t2.TotalParameterCount; + pSMB_rsp->t2.Reserved = 0; + pSMB_rsp->t2.ParameterCount = cpu_to_le16(2); + pSMB_rsp->t2.ParameterOffset = cpu_to_le16(56); + pSMB_rsp->t2.ParameterDisplacement = 0; + pSMB_rsp->t2.DataCount = pSMB_rsp->t2.TotalDataCount; + pSMB_rsp->t2.DataOffset = cpu_to_le16(data_offset); + pSMB_rsp->t2.DataDisplacement = 0; + pSMB_rsp->t2.SetupCount = 0; + pSMB_rsp->t2.Reserved1 = 0; + + /* 2 for parameter count + 112 data count + 3 pad (1 pad1 + 2 pad2)*/ + pSMB_rsp->ByteCount = cpu_to_le16(117); + pSMB_rsp->Reserved2 = 0; + inc_rfc1001_len(&pSMB_rsp->hdr, + (pSMB_rsp->hdr.WordCount * 2 + 117)); + +free_path: + path_put(&path); +out: + switch (err) { + case 0: + break; + case -ENOSPC: + pSMB_rsp->hdr.Status.CifsError = STATUS_DISK_FULL; + break; + case -EINVAL: + pSMB_rsp->hdr.Status.CifsError = STATUS_NO_SUCH_USER; + break; + case -EACCES: + pSMB_rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + break; + case -ENOENT: + pSMB_rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_NOT_FOUND; + break; + case -EBUSY: + pSMB_rsp->hdr.Status.CifsError = STATUS_DELETE_PENDING; + break; + default: + pSMB_rsp->hdr.Status.CifsError = + STATUS_UNEXPECTED_IO_ERROR; + } + + if (err && fp) + ksmbd_close_fd(work, fp->volatile_id); + kfree(name); + ksmbd_revert_fsids(work); + return err; +} + +/** + * smb_posix_unlink() - handler for posix file delete + * @work: smb work containing trans2 posix delete command + * + * Return: 0 on success, otherwise error + */ +static int smb_posix_unlink(struct ksmbd_work *work) +{ + struct smb_com_trans2_spi_req *req = work->request_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + struct unlink_psx_rsp *psx_rsp = NULL; + struct ksmbd_share_config *share = work->tcon->share_conf; + char *name; + int rc = 0; + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "returning as user does not have permission to write\n"); + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + return -EACCES; + } + + name = smb_get_name(share, req->FileName, PATH_MAX, work, false); + if (IS_ERR(name)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(name); + } + + rc = ksmbd_vfs_remove_file(work, name); + if (rc < 0) + goto out; + + psx_rsp = (struct unlink_psx_rsp *)((char *)rsp + + sizeof(struct smb_com_trans2_rsp)); + psx_rsp->EAErrorOffset = cpu_to_le16(0); + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = cpu_to_le16(0); + rsp->t2.ParameterCount = rsp->t2.TotalParameterCount; + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = cpu_to_le16(0); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + + /* 2 for parameter count + 1 pad1*/ + rsp->ByteCount = cpu_to_le16(3); + rsp->Pad = 0; + inc_rfc1001_len(&rsp->hdr, rsp->hdr.WordCount * 2 + 3); + +out: + if (rc) + rsp->hdr.Status.CifsError = STATUS_UNEXPECTED_IO_ERROR; + + kfree(name); + return rc; +} + +/** + * smb_set_time_pathinfo() - handler for setting time using set path info + * @work: smb work containing set path info command + * + * Return: 0 on success, otherwise error + */ +static int smb_set_time_pathinfo(struct ksmbd_work *work) +{ + struct smb_com_trans2_spi_req *req = work->request_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + struct file_basic_info *info; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct iattr attrs; + char *name; + int err = 0; + + name = smb_get_name(share, req->FileName, PATH_MAX, work, false); + if (IS_ERR(name)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(name); + } + + info = (struct file_basic_info *)(((char *) &req->hdr.Protocol) + + le16_to_cpu(req->DataOffset)); + + attrs.ia_valid = 0; + if (le64_to_cpu(info->LastAccessTime)) { + attrs.ia_atime = smb_NTtimeToUnix(info->LastAccessTime); + attrs.ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET); + } + + if (le64_to_cpu(info->ChangeTime)) { + attrs.ia_ctime = smb_NTtimeToUnix(info->ChangeTime); + attrs.ia_valid |= ATTR_CTIME; + } + + if (le64_to_cpu(info->LastWriteTime)) { + attrs.ia_mtime = smb_NTtimeToUnix(info->LastWriteTime); + attrs.ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET); + } + /* TODO: check dos mode and acl bits if req->Attributes nonzero */ + + if (!attrs.ia_valid) + goto done; + + err = ksmbd_vfs_setattr(work, name, 0, &attrs); + if (err) { + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + return err; + } + +done: + ksmbd_debug(SMB, "%s setattr done\n", name); + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = 0; + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = rsp->t2.TotalParameterCount; + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = 0; + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + + /* 3 pad (1 pad1 + 2 pad2)*/ + rsp->ByteCount = cpu_to_le16(3); + inc_rfc1001_len(&rsp->hdr, rsp->hdr.WordCount * 2 + 3); + + kfree(name); + return 0; +} + +/** + * smb_set_unix_pathinfo() - handler for setting unix path info(setattr) + * @work: smb work containing set path info command + * + * Return: 0 on success, otherwise error + */ +static int smb_set_unix_pathinfo(struct ksmbd_work *work) +{ + struct smb_com_trans2_spi_req *req = work->request_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + struct file_unix_basic_info *unix_info; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct path path; + struct iattr attrs; + char *name; + int err = 0; + + name = smb_get_name(share, req->FileName, PATH_MAX, work, false); + if (IS_ERR(name)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(name); + } + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + err = kern_path(name, 0, &path); + if (err) { + ksmbd_revert_fsids(work); + kfree(name); + return -ENOENT; + } + + unix_info = (struct file_unix_basic_info *) + (((char *) &req->hdr.Protocol) + le16_to_cpu(req->DataOffset)); + attrs.ia_valid = 0; + attrs.ia_mode = 0; + err = unix_info_to_attr(unix_info, mnt_user_ns(path.mnt), &attrs); + path_put(&path); + ksmbd_revert_fsids(work); + if (err) + goto out; + + err = ksmbd_vfs_setattr(work, name, 0, &attrs); + if (err) + goto out; + /* setattr success, prepare response */ + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = 0; + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = rsp->t2.TotalParameterCount; + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = 0; + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + + /* 3 pad (1 pad1 + 2 pad2)*/ + rsp->ByteCount = cpu_to_le16(3); + inc_rfc1001_len(&rsp->hdr, rsp->hdr.WordCount * 2 + 3); + +out: + kfree(name); + if (err) { + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + return err; + } + return 0; +} + +/** + * smb_set_ea() - handler for setting extended attributes using set path + * info command + * @work: smb work containing set path info command + * + * Return: 0 on success, otherwise error + */ +static int smb_set_ea(struct ksmbd_work *work) +{ + struct smb_com_trans2_spi_req *req = work->request_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct fealist *eabuf; + struct fea *ea; + char *fname, *attr_name = NULL, *value; + int rc = 0, list_len, i, next = 0; + + fname = smb_get_name(share, req->FileName, PATH_MAX, work, false); + if (IS_ERR(fname)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(fname); + } + + eabuf = (struct fealist *)(((char *) &req->hdr.Protocol) + + le16_to_cpu(req->DataOffset)); + + list_len = le32_to_cpu(eabuf->list_len) - 4; + ea = (struct fea *)eabuf->list; + + for (i = 0; list_len >= 0 && ea->name_len != 0; i++, list_len -= next) { + if (ea->name_len > (XATTR_NAME_MAX - XATTR_USER_PREFIX_LEN)) { + rc = -EINVAL; + goto out; + } + + next = ea->name_len + le16_to_cpu(ea->value_len) + 4; + + attr_name = kmalloc(XATTR_NAME_MAX + 1, GFP_KERNEL); + if (!attr_name) { + rc = -ENOMEM; + goto out; + } + + memcpy(attr_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN); + memcpy(&attr_name[XATTR_USER_PREFIX_LEN], ea->name, + ea->name_len); + attr_name[XATTR_USER_PREFIX_LEN + ea->name_len] = '\0'; + value = (char *)&ea->name + ea->name_len + 1; + ksmbd_debug(SMB, "name: <%s>, name_len %u, value_len %u\n", + ea->name, ea->name_len, le16_to_cpu(ea->value_len)); + + rc = ksmbd_vfs_fsetxattr(work, fname, attr_name, value, + le16_to_cpu(ea->value_len), + 0); + if (rc < 0) { + kfree(attr_name); + rsp->hdr.Status.CifsError = + STATUS_UNEXPECTED_IO_ERROR; + goto out; + } + kfree(attr_name); + ea += next; + } + + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = cpu_to_le16(0); + rsp->t2.ParameterCount = rsp->t2.TotalParameterCount; + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = cpu_to_le16(0); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + + /* 2 for parameter count + 1 pad1*/ + rsp->ByteCount = cpu_to_le16(3); + rsp->Pad = 0; + inc_rfc1001_len(&rsp->hdr, rsp->hdr.WordCount * 2 + 3); + +out: + kfree(fname); + return rc; +} + +/** + * smb_set_file_size_pinfo() - handler for setting eof or truncate using + * trans2 set path info command + * @work: smb work containing set path info command + * + * Return: 0 on success, otherwise error + */ +static int smb_set_file_size_pinfo(struct ksmbd_work *work) +{ + struct smb_com_trans2_spi_req *req = work->request_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct file_end_of_file_info *eofinfo; + struct iattr attr; + char *name = NULL; + loff_t newsize; + int rc = 0; + + name = smb_get_name(share, req->FileName, PATH_MAX, work, false); + if (IS_ERR(name)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(name); + } + + eofinfo = (struct file_end_of_file_info *) + (((char *) &req->hdr.Protocol) + le16_to_cpu(req->DataOffset)); + newsize = le64_to_cpu(eofinfo->FileSize); + attr.ia_valid = ATTR_SIZE; + attr.ia_size = newsize; + rc = ksmbd_vfs_setattr(work, name, 0, &attr); + if (rc) { + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + goto out; + } + ksmbd_debug(SMB, "%s truncated to newsize %lld\n", + name, newsize); + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = 0; + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = rsp->t2.TotalParameterCount; + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = 0; + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + + /* 2 for parameter count + 1 pad1*/ + rsp->ByteCount = cpu_to_le16(3); + inc_rfc1001_len(&rsp->hdr, rsp->hdr.WordCount * 2 + 3); + +out: + kfree(name); + return rc; +} + +/** + * smb_creat_hardlink() - handler for creating hardlink + * @work: smb work containing set path info command buffer + * + * Return: 0 on success, otherwise error + */ +static int smb_creat_hardlink(struct ksmbd_work *work) +{ + struct smb_com_trans2_spi_req *req = work->request_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + char *oldname, *newname, *oldname_offset; + int err; + + newname = smb_get_name(share, req->FileName, PATH_MAX, work, false); + if (IS_ERR(newname)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(newname); + } + + oldname_offset = ((char *)&req->hdr.Protocol) + + le16_to_cpu(req->DataOffset); + oldname = smb_get_name(share, oldname_offset, PATH_MAX, work, false); + if (IS_ERR(oldname)) { + err = PTR_ERR(oldname); + oldname = NULL; + goto out; + } + ksmbd_debug(SMB, "oldname %s, newname %s\n", oldname, newname); + + err = ksmbd_vfs_link(work, oldname, newname); + if (err < 0) { + if (err == -EACCES) + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + else + rsp->hdr.Status.CifsError = STATUS_NOT_SAME_DEVICE; + goto out; + } + + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = 0; + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = rsp->t2.TotalParameterCount; + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = 0; + rsp->t2.DataOffset = 0; + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + rsp->ByteCount = cpu_to_le16(3); + inc_rfc1001_len(&rsp->hdr, rsp->hdr.WordCount * 2 + 3); +out: + kfree(newname); + kfree(oldname); + return err; +} + +/** + * smb_creat_symlink() - handler for creating symlink + * @work: smb work containing set path info command buffer + * + * Return: 0 on success, otherwise error + */ +static int smb_creat_symlink(struct ksmbd_work *work) +{ + struct smb_com_trans2_spi_req *req = work->request_buf; + struct smb_com_trans2_spi_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + char *name, *symname, *name_offset; + bool is_unicode = is_smbreq_unicode(&req->hdr); + int err; + + symname = smb_get_name(share, req->FileName, PATH_MAX, work, false); + if (IS_ERR(symname)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(symname); + } + + name_offset = ((char *)&req->hdr.Protocol) + + le16_to_cpu(req->DataOffset); + name = smb_strndup_from_utf16(name_offset, PATH_MAX, is_unicode, + work->conn->local_nls); + if (IS_ERR(name)) { + kfree(symname); + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + return PTR_ERR(name); + } + ksmbd_debug(SMB, "name %s, symname %s\n", name, symname); + + err = ksmbd_vfs_symlink(work, name, symname); + if (err < 0) { + if (err == -ENOSPC) + rsp->hdr.Status.CifsError = STATUS_DISK_FULL; + else if (err == -EEXIST) + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_COLLISION; + else + rsp->hdr.Status.CifsError = STATUS_NOT_SAME_DEVICE; + } else + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = 0; + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = rsp->t2.TotalParameterCount; + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = 0; + rsp->t2.DataOffset = 0; + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + rsp->ByteCount = cpu_to_le16(3); + inc_rfc1001_len(&rsp->hdr, rsp->hdr.WordCount * 2 + 3); + kfree(name); + kfree(symname); + return err; +} + +/** + * set_path_info() - handler for trans2 set path info sub commands + * @work: smb work containing set path info command + * + * Return: 0 on success, otherwise error + */ +static int set_path_info(struct ksmbd_work *work) +{ + struct smb_com_trans2_spi_req *pSMB_req = work->request_buf; + struct smb_com_trans2_spi_rsp *pSMB_rsp = work->response_buf; + __u16 info_level, total_param; + int err = 0; + + info_level = le16_to_cpu(pSMB_req->InformationLevel); + total_param = le16_to_cpu(pSMB_req->TotalParameterCount); + if (total_param < 7) { + pSMB_rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + pr_err("invalid total parameter for info_level 0x%x\n", + total_param); + return -EINVAL; + } + + switch (info_level) { + case SMB_POSIX_OPEN: + err = smb_posix_open(work); + break; + case SMB_POSIX_UNLINK: + err = smb_posix_unlink(work); + break; + case SMB_SET_FILE_UNIX_HLINK: + err = smb_creat_hardlink(work); + break; + case SMB_SET_FILE_UNIX_LINK: + err = smb_creat_symlink(work); + break; + case SMB_SET_FILE_BASIC_INFO: + /* fall through */ + case SMB_SET_FILE_BASIC_INFO2: + err = smb_set_time_pathinfo(work); + break; + case SMB_SET_FILE_UNIX_BASIC: + err = smb_set_unix_pathinfo(work); + break; + case SMB_SET_FILE_EA: + err = smb_set_ea(work); + break; + case SMB_SET_POSIX_ACL: + err = smb_set_acl(work); + break; + case SMB_SET_FILE_END_OF_FILE_INFO2: + /* fall through */ + case SMB_SET_FILE_END_OF_FILE_INFO: + err = smb_set_file_size_pinfo(work); + break; + default: + ksmbd_debug(SMB, "info level = %x not implemented yet\n", + info_level); + pSMB_rsp->hdr.Status.CifsError = STATUS_NOT_IMPLEMENTED; + return -EOPNOTSUPP; + } + + if (err < 0) + ksmbd_debug(SMB, "info_level 0x%x failed, err %d\n", + info_level, err); + return err; +} +static int readdir_info_level_struct_sz(int info_level) +{ + switch (info_level) { + case SMB_FIND_FILE_INFO_STANDARD: + return sizeof(struct find_info_standard); + case SMB_FIND_FILE_QUERY_EA_SIZE: + return sizeof(struct find_info_query_ea_size); + case SMB_FIND_FILE_DIRECTORY_INFO: + return sizeof(struct file_directory_info); + case SMB_FIND_FILE_FULL_DIRECTORY_INFO: + return sizeof(struct file_full_directory_info); + case SMB_FIND_FILE_NAMES_INFO: + return sizeof(struct file_names_info); + case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: + return sizeof(struct file_both_directory_info); + case SMB_FIND_FILE_ID_FULL_DIR_INFO: + return sizeof(struct file_id_full_dir_info); + case SMB_FIND_FILE_ID_BOTH_DIR_INFO: + return sizeof(struct file_id_both_directory_info); + case SMB_FIND_FILE_UNIX: + return sizeof(struct file_unix_info); + default: + return -EOPNOTSUPP; + } +} + +/** + * smb_populate_readdir_entry() - encode directory entry in smb response buffer + * @conn: connection instance + * @info_level: smb information level + * @d_info: structure included variables for query dir + * @ksmbd_kstat: ksmbd wrapper of dirent stat information + * + * if directory has many entries, find first can't read it fully. + * find next might be called multiple times to read remaining dir entries + * + * Return: 0 on success, otherwise error + */ +static int smb_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, + struct ksmbd_dir_info *d_info, struct ksmbd_kstat *ksmbd_kstat) +{ + int next_entry_offset; + char *conv_name; + int conv_len; + int struct_sz; + + struct_sz = readdir_info_level_struct_sz(info_level); + if (struct_sz == -EOPNOTSUPP) + return -EOPNOTSUPP; + + conv_name = ksmbd_convert_dir_info_name(d_info, + conn->local_nls, + &conv_len); + if (!conv_name) + return -ENOMEM; + + next_entry_offset = ALIGN(struct_sz - 1 + conv_len, + KSMBD_DIR_INFO_ALIGNMENT); + + if (next_entry_offset > d_info->out_buf_len) { + kfree(conv_name); + d_info->out_buf_len = -1; + return -ENOSPC; + } + + switch (info_level) { + case SMB_FIND_FILE_INFO_STANDARD: + { + struct find_info_standard *fsinfo; + + fsinfo = (struct find_info_standard *)(d_info->wptr); + unix_to_dos_time( + ksmbd_NTtimeToUnix( + cpu_to_le64(ksmbd_kstat->create_time)), + &fsinfo->CreationTime, + &fsinfo->CreationDate); + unix_to_dos_time(ksmbd_kstat->kstat->atime, + &fsinfo->LastAccessTime, + &fsinfo->LastAccessDate); + unix_to_dos_time(ksmbd_kstat->kstat->mtime, + &fsinfo->LastWriteTime, + &fsinfo->LastWriteDate); + fsinfo->DataSize = cpu_to_le32(ksmbd_kstat->kstat->size); + fsinfo->AllocationSize = + cpu_to_le32(ksmbd_kstat->kstat->blocks << 9); + fsinfo->Attributes = + cpu_to_le16(S_ISDIR(ksmbd_kstat->kstat->mode) ? + ATTR_DIRECTORY : ATTR_ARCHIVE); + fsinfo->FileNameLength = cpu_to_le16(conv_len); + memcpy(fsinfo->FileName, conv_name, conv_len); + + break; + } + case SMB_FIND_FILE_QUERY_EA_SIZE: + { + struct find_info_query_ea_size *fesize; + + fesize = (struct find_info_query_ea_size *)(d_info->wptr); + unix_to_dos_time( + ksmbd_NTtimeToUnix( + cpu_to_le64(ksmbd_kstat->create_time)), + &fesize->CreationTime, + &fesize->CreationDate); + unix_to_dos_time(ksmbd_kstat->kstat->atime, + &fesize->LastAccessTime, + &fesize->LastAccessDate); + unix_to_dos_time(ksmbd_kstat->kstat->mtime, + &fesize->LastWriteTime, + &fesize->LastWriteDate); + + fesize->DataSize = + cpu_to_le32(ksmbd_kstat->kstat->size); + fesize->AllocationSize = + cpu_to_le32(ksmbd_kstat->kstat->blocks << 9); + fesize->Attributes = + cpu_to_le16(S_ISDIR(ksmbd_kstat->kstat->mode) ? + ATTR_DIRECTORY : ATTR_ARCHIVE); + fesize->EASize = 0; + fesize->FileNameLength = (__u8)(conv_len); + memcpy(fesize->FileName, conv_name, conv_len); + + break; + } + case SMB_FIND_FILE_DIRECTORY_INFO: + { + struct file_directory_info *fdinfo = NULL; + + fdinfo = (struct file_directory_info *) + ksmbd_vfs_init_kstat(&d_info->wptr, ksmbd_kstat); + fdinfo->FileNameLength = cpu_to_le32(conv_len); + memcpy(fdinfo->FileName, conv_name, conv_len); + fdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + memset((char *)fdinfo + struct_sz - 1 + conv_len, + '\0', + next_entry_offset - struct_sz - 1 + conv_len); + break; + } + case SMB_FIND_FILE_FULL_DIRECTORY_INFO: + { + struct file_full_directory_info *ffdinfo = NULL; + + ffdinfo = (struct file_full_directory_info *) + ksmbd_vfs_init_kstat(&d_info->wptr, ksmbd_kstat); + ffdinfo->FileNameLength = cpu_to_le32(conv_len); + ffdinfo->EaSize = 0; + memcpy(ffdinfo->FileName, conv_name, conv_len); + ffdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + memset((char *)ffdinfo + struct_sz - 1 + conv_len, + '\0', + next_entry_offset - struct_sz - 1 + conv_len); + break; + } + case SMB_FIND_FILE_NAMES_INFO: + { + struct file_names_info *fninfo = NULL; + + fninfo = (struct file_names_info *)(d_info->wptr); + fninfo->FileNameLength = cpu_to_le32(conv_len); + memcpy(fninfo->FileName, conv_name, conv_len); + fninfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + memset((char *)fninfo + struct_sz - 1 + conv_len, + '\0', + next_entry_offset - struct_sz - 1 + conv_len); + + break; + } + case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: + { + struct file_both_directory_info *fbdinfo = NULL; + + fbdinfo = (struct file_both_directory_info *) + ksmbd_vfs_init_kstat(&d_info->wptr, ksmbd_kstat); + fbdinfo->FileNameLength = cpu_to_le32(conv_len); + fbdinfo->EaSize = 0; + fbdinfo->ShortNameLength = 0; + fbdinfo->Reserved = 0; + memcpy(fbdinfo->FileName, conv_name, conv_len); + fbdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + memset((char *)fbdinfo + struct_sz - 1 + conv_len, + '\0', + next_entry_offset - struct_sz - 1 + conv_len); + break; + } + case SMB_FIND_FILE_ID_FULL_DIR_INFO: + { + struct file_id_full_dir_info *dinfo = NULL; + + dinfo = (struct file_id_full_dir_info *) + ksmbd_vfs_init_kstat(&d_info->wptr, ksmbd_kstat); + dinfo->FileNameLength = cpu_to_le32(conv_len); + dinfo->EaSize = 0; + dinfo->Reserved = 0; + dinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); + memcpy(dinfo->FileName, conv_name, conv_len); + dinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + memset((char *)dinfo + struct_sz - 1 + conv_len, + '\0', + next_entry_offset - struct_sz - 1 + conv_len); + break; + } + case SMB_FIND_FILE_ID_BOTH_DIR_INFO: + { + struct file_id_both_directory_info *fibdinfo = NULL; + + fibdinfo = (struct file_id_both_directory_info *) + ksmbd_vfs_init_kstat(&d_info->wptr, ksmbd_kstat); + fibdinfo->FileNameLength = cpu_to_le32(conv_len); + fibdinfo->EaSize = 0; + fibdinfo->ShortNameLength = 0; + fibdinfo->Reserved = 0; + fibdinfo->Reserved2 = 0; + fibdinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); + memcpy(fibdinfo->FileName, conv_name, conv_len); + fibdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + memset((char *)fibdinfo + struct_sz - 1 + conv_len, + '\0', + next_entry_offset - struct_sz - 1 + conv_len); + + break; + } + case SMB_FIND_FILE_UNIX: + { + struct file_unix_info *finfo = NULL; + struct file_unix_basic_info *unix_info; + + finfo = (struct file_unix_info *)(d_info->wptr); + finfo->ResumeKey = 0; + unix_info = (struct file_unix_basic_info *)((char *)finfo + 8); + init_unix_info(unix_info, &init_user_ns, ksmbd_kstat->kstat); + /* include null terminator */ + memcpy(finfo->FileName, conv_name, conv_len + 2); + next_entry_offset += 2; + finfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + memset((char *)finfo + struct_sz - 1 + conv_len, + '\0', + next_entry_offset - struct_sz - 1 + conv_len); + break; + } + } + + d_info->num_entry++; + d_info->last_entry_offset = d_info->data_count; + d_info->data_count += next_entry_offset; + d_info->out_buf_len -= next_entry_offset; + d_info->wptr = (char *)(d_info->wptr) + next_entry_offset; + kfree(conv_name); + + ksmbd_debug(SMB, "info_level : %d, buf_len :%d, next_offset : %d, data_count : %d\n", + info_level, d_info->out_buf_len, + next_entry_offset, d_info->data_count); + return 0; +} + +/** + * ksmbd_fill_dirent() - populates a dirent details in readdir + * @ctx: dir_context information + * @name: dirent name + * @namelen: dirent name length + * @offset: dirent offset in directory + * @ino: dirent inode number + * @d_type: dirent type + * + * Return: 0 on success, otherwise -EINVAL + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) +static bool ksmbd_fill_dirent(struct dir_context *ctx, const char *name, int namlen, +#else +static int ksmbd_fill_dirent(struct dir_context *ctx, const char *name, int namlen, +#endif + loff_t offset, u64 ino, unsigned int d_type) +{ + struct ksmbd_readdir_data *buf = + container_of(ctx, struct ksmbd_readdir_data, ctx); + struct ksmbd_dirent *de = (void *)(buf->dirent + buf->used); + unsigned int reclen; + + reclen = ALIGN(sizeof(struct ksmbd_dirent) + namlen, sizeof(u64)); + if (buf->used + reclen > PAGE_SIZE) + return -EINVAL; + + de->namelen = namlen; + de->offset = offset; + de->ino = ino; + de->d_type = d_type; + memcpy(de->name, name, namlen); + buf->used += reclen; + + return 0; +} + +/** + * find_first() - smb readdir command + * @work: smb work containing find first request params + * + * Return: 0 on success, otherwise error + */ +static int find_first(struct ksmbd_work *work) +{ + struct smb_hdr *rsp_hdr = work->response_buf; + struct ksmbd_conn *conn = work->conn; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct smb_com_trans2_req *req = work->request_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + struct smb_com_trans2_ffirst_req_params *req_params; + struct smb_com_trans2_ffirst_rsp_parms *params = NULL; + struct path path; + struct ksmbd_dirent *de; + struct ksmbd_file *dir_fp = NULL; + struct kstat kstat; + struct ksmbd_kstat ksmbd_kstat; + struct ksmbd_dir_info d_info; + int params_count = sizeof(struct smb_com_trans2_ffirst_rsp_parms); + int data_alignment_offset = 0; + int rc = 0, reclen = 0; + int srch_cnt = 0; + char *dirpath = NULL; + char *srch_ptr = NULL; + int header_size; + int struct_sz; + + memset(&d_info, 0, sizeof(struct ksmbd_dir_info)); + + if (ksmbd_override_fsids(work)) { + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + return -ENOMEM; + } + + req_params = (struct smb_com_trans2_ffirst_req_params *) + (work->request_buf + le16_to_cpu(req->ParameterOffset) + 4); + dirpath = smb_get_dir_name(share, req_params->FileName, PATH_MAX, + work, &srch_ptr); + if (IS_ERR(dirpath)) { + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + rc = PTR_ERR(dirpath); + goto err_out; + } + + ksmbd_debug(SMB, "complete dir path = %s\n", dirpath); + rc = ksmbd_vfs_kern_path(work, dirpath, LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY, + &path, 0); + if (rc < 0) { + ksmbd_debug(SMB, "cannot create vfs root path <%s> %d\n", + dirpath, rc); + goto err_free_dirpath; + } else { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + if (inode_permission(mnt_user_ns(path.mnt), + d_inode(path.dentry), + MAY_READ | MAY_EXEC)) { +#else + if (inode_permission(d_inode(path.dentry), + MAY_READ | MAY_EXEC)) { +#endif + rc = -EACCES; + path_put(&path); + goto err_free_dirpath; + } + } + + if (d_is_symlink(path.dentry)) { + rc = -EACCES; + path_put(&path); + goto err_free_dirpath; + } + + dir_fp = ksmbd_vfs_dentry_open(work, &path, O_RDONLY, 0, 1); + if (IS_ERR(dir_fp)) { + ksmbd_debug(SMB, "dir dentry open failed with rc=%d\n", rc); + path_put(&path); + rc = -EINVAL; + dir_fp = NULL; + goto err_free_dirpath; + } + + write_lock(&dir_fp->f_ci->m_lock); + list_add(&dir_fp->node, &dir_fp->f_ci->m_fp_list); + write_unlock(&dir_fp->f_ci->m_lock); + + set_ctx_actor(&dir_fp->readdir_data.ctx, ksmbd_fill_dirent); + dir_fp->readdir_data.dirent = (void *)__get_free_page(GFP_KERNEL); + if (!dir_fp->readdir_data.dirent) { + rc = -ENOMEM; + goto err_free_dirpath; + } + + dir_fp->filename = dirpath; + dir_fp->readdir_data.used = 0; + dir_fp->dirent_offset = 0; + dir_fp->readdir_data.file_attr = + le16_to_cpu(req_params->SearchAttributes); + + if (params_count % 4) + data_alignment_offset = 4 - params_count % 4; + + d_info.smb1_name = kmalloc(NAME_MAX + 1, GFP_KERNEL); + if (!d_info.smb1_name) { + rc = -ENOMEM; + goto err_out; + } + d_info.wptr = (char *)((char *)rsp + sizeof(struct smb_com_trans2_rsp) + + params_count + data_alignment_offset); + + header_size = sizeof(struct smb_com_trans2_rsp) + params_count + + data_alignment_offset; + + + struct_sz = readdir_info_level_struct_sz(le16_to_cpu(req_params->InformationLevel)); + + if (struct_sz < 0) { + rc = -EFAULT; + goto err_out; + } + + /* When search count is zero, respond only 1 entry. */ + srch_cnt = le16_to_cpu(req_params->SearchCount); + if (!srch_cnt) + d_info.out_buf_len = struct_sz + header_size; + else + d_info.out_buf_len = min_t(int, srch_cnt * struct_sz + header_size, + MAX_CIFS_LOOKUP_BUFFER_SIZE - header_size); + + + /* reserve dot and dotdot entries in head of buffer in first response */ + if (!*srch_ptr || is_asterisk(srch_ptr)) { + rc = ksmbd_populate_dot_dotdot_entries(work, + le16_to_cpu(req_params->InformationLevel), + dir_fp, + &d_info, + srch_ptr, + smb_populate_readdir_entry); + if (rc) + goto err_out; + } + + do { + if (dir_fp->dirent_offset >= dir_fp->readdir_data.used) { + dir_fp->dirent_offset = 0; + dir_fp->readdir_data.used = 0; + rc = iterate_dir(dir_fp->filp, &dir_fp->readdir_data.ctx); + if (rc < 0) { + ksmbd_debug(SMB, "err : %d\n", rc); + goto err_out; + } + + if (!dir_fp->readdir_data.used) { + free_page((unsigned long) + (dir_fp->readdir_data.dirent)); + dir_fp->readdir_data.dirent = NULL; + break; + } + + de = (struct ksmbd_dirent *) + ((char *)dir_fp->readdir_data.dirent); + } else { + de = (struct ksmbd_dirent *) + ((char *)dir_fp->readdir_data.dirent + + dir_fp->dirent_offset); + } + + reclen = ALIGN(sizeof(struct ksmbd_dirent) + de->namelen, + sizeof(__le64)); + dir_fp->dirent_offset += reclen; + + if (dir_fp->readdir_data.file_attr & + SMB_SEARCH_ATTRIBUTE_DIRECTORY && de->d_type != DT_DIR) + continue; + + ksmbd_kstat.kstat = &kstat; + + if (de->namelen > NAME_MAX) { + pr_err("filename length exceeds 255 bytes.\n"); + continue; + } + + if (!strncmp(de->name, ".", de->namelen) || + !strncmp(de->name, "..", de->namelen)) + continue; + + memcpy(d_info.smb1_name, de->name, de->namelen); + d_info.smb1_name[de->namelen] = '\0'; + d_info.name = (const char *)d_info.smb1_name; + d_info.name_len = de->namelen; + rc = ksmbd_vfs_readdir_name(work, + file_mnt_user_ns(dir_fp->filp), + &ksmbd_kstat, + de->name, + de->namelen, + dirpath); + if (rc) { + ksmbd_debug(SMB, "Cannot read dirent: %d\n", rc); + continue; + } + + if (ksmbd_share_veto_filename(share, d_info.name)) { + ksmbd_debug(SMB, "Veto filename %s\n", d_info.name); + continue; + } + + if (match_pattern(d_info.name, d_info.name_len, srch_ptr)) { + rc = smb_populate_readdir_entry(conn, + le16_to_cpu(req_params->InformationLevel), + &d_info, + &ksmbd_kstat); + if (rc == -ENOSPC) + break; + else if (rc) + goto err_out; + } + } while (d_info.out_buf_len >= 0); + + if (!d_info.data_count && *srch_ptr) { + ksmbd_debug(SMB, "There is no entry matched with the search pattern\n"); + rc = -ENOENT; + goto err_out; + } + + if (d_info.out_buf_len < 0) + dir_fp->dirent_offset -= reclen; + + params = (struct smb_com_trans2_ffirst_rsp_parms *)((char *)rsp + + sizeof(struct smb_com_trans2_rsp)); + params->SearchHandle = dir_fp->volatile_id; + params->SearchCount = cpu_to_le16(d_info.num_entry); + params->LastNameOffset = cpu_to_le16(d_info.last_entry_offset); + + if (d_info.out_buf_len < 0) { + ksmbd_debug(SMB, "continue search\n"); + params->EndofSearch = cpu_to_le16(0); + } else { + ksmbd_debug(SMB, "end of search\n"); + params->EndofSearch = cpu_to_le16(1); + path_put(&(dir_fp->filp->f_path)); + if (le16_to_cpu(req_params->SearchFlags) & + CIFS_SEARCH_CLOSE_AT_END) + ksmbd_close_fd(work, dir_fp->volatile_id); + } + params->EAErrorOffset = cpu_to_le16(0); + + rsp_hdr->WordCount = 0x0A; + rsp->t2.TotalParameterCount = cpu_to_le16(params_count); + rsp->t2.TotalDataCount = cpu_to_le16(d_info.data_count); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(params_count); + rsp->t2.ParameterOffset = + cpu_to_le16(sizeof(struct smb_com_trans2_rsp) - 4); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(d_info.data_count); + rsp->t2.DataOffset = cpu_to_le16(sizeof(struct smb_com_trans2_rsp) + + params_count + data_alignment_offset - 4); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + rsp->Pad = 0; + rsp->ByteCount = cpu_to_le16(d_info.data_count + + params_count + 1 /*pad*/ + data_alignment_offset); + memset((char *)rsp + sizeof(struct smb_com_trans2_rsp) + params_count, + '\0', 2); + inc_rfc1001_len(rsp_hdr, (10 * 2 + d_info.data_count + + params_count + 1 + data_alignment_offset)); + kfree(srch_ptr); + kfree(d_info.smb1_name); + ksmbd_revert_fsids(work); + return 0; + +err_free_dirpath: + kfree(dirpath); +err_out: + if (rc == -EINVAL) + rsp_hdr->Status.CifsError = STATUS_INVALID_PARAMETER; + else if (rc == -EACCES || rc == -EXDEV) + rsp_hdr->Status.CifsError = STATUS_ACCESS_DENIED; + else if (rc == -ENOENT) + rsp_hdr->Status.CifsError = STATUS_NO_SUCH_FILE; + else if (rc == -EBADF) + rsp_hdr->Status.CifsError = STATUS_FILE_CLOSED; + else if (rc == -ENOMEM) + rsp_hdr->Status.CifsError = STATUS_NO_MEMORY; + else if (rc == -EFAULT) + rsp_hdr->Status.CifsError = STATUS_INVALID_LEVEL; + if (!rsp->hdr.Status.CifsError) + rsp->hdr.Status.CifsError = STATUS_UNEXPECTED_IO_ERROR; + + if (dir_fp) { + if (dir_fp->readdir_data.dirent) { + free_page((unsigned long)(dir_fp->readdir_data.dirent)); + dir_fp->readdir_data.dirent = NULL; + } + path_put(&(dir_fp->filp->f_path)); + ksmbd_close_fd(work, dir_fp->volatile_id); + } + + kfree(srch_ptr); + kfree(d_info.smb1_name); + ksmbd_revert_fsids(work); + return 0; +} + +/** + * find_next() - smb next readdir command + * @work: smb work containing find next request params + * + * if directory has many entries, find first can't read it fully. + * find next might be called multiple times to read remaining dir entries + * + * Return: 0 on success, otherwise error + */ +static int find_next(struct ksmbd_work *work) +{ + struct smb_hdr *rsp_hdr = work->response_buf; + struct ksmbd_conn *conn = work->conn; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct smb_com_trans2_req *req = work->request_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + struct smb_com_trans2_fnext_req_params *req_params; + struct smb_com_trans2_fnext_rsp_params *params = NULL; + struct ksmbd_dirent *de; + struct ksmbd_file *dir_fp; + struct kstat kstat; + struct ksmbd_kstat ksmbd_kstat; + struct ksmbd_dir_info d_info; + int params_count = sizeof(struct smb_com_trans2_fnext_rsp_params); + int data_alignment_offset = 0; + int rc = 0, reclen = 0; + __u16 sid; + int header_size, srch_cnt, struct_sz; + + memset(&d_info, 0, sizeof(struct ksmbd_dir_info)); + + req_params = (struct smb_com_trans2_fnext_req_params *) + (work->request_buf + le16_to_cpu(req->ParameterOffset) + 4); + sid = req_params->SearchHandle; + + dir_fp = ksmbd_lookup_fd_fast(work, sid); + if (!dir_fp) { + ksmbd_debug(SMB, "error invalid sid\n"); + rc = -EINVAL; + goto err_out; + } + + set_ctx_actor(&dir_fp->readdir_data.ctx, ksmbd_fill_dirent); + + if (params_count % 4) + data_alignment_offset = 4 - params_count % 4; + + d_info.smb1_name = kmalloc(NAME_MAX + 1, GFP_KERNEL); + if (!d_info.smb1_name) { + rc = -ENOMEM; + goto err_out; + } + d_info.wptr = (char *)((char *)rsp + sizeof(struct smb_com_trans2_rsp) + + params_count + data_alignment_offset); + + header_size = sizeof(struct smb_com_trans2_rsp) + params_count + + data_alignment_offset; + + srch_cnt = le16_to_cpu(req_params->SearchCount); + struct_sz = readdir_info_level_struct_sz(le16_to_cpu(req_params->InformationLevel)); + + if (struct_sz < 0) { + rc = -EFAULT; + goto err_out; + } + + d_info.out_buf_len = min_t(int, srch_cnt * struct_sz + header_size, + MAX_CIFS_LOOKUP_BUFFER_SIZE - header_size); + do { + if (dir_fp->dirent_offset >= dir_fp->readdir_data.used) { + dir_fp->dirent_offset = 0; + dir_fp->readdir_data.used = 0; + rc = iterate_dir(dir_fp->filp, &dir_fp->readdir_data.ctx); + if (rc < 0) { + ksmbd_debug(SMB, "err : %d\n", rc); + goto err_out; + } + + if (!dir_fp->readdir_data.used) { + free_page((unsigned long) + (dir_fp->readdir_data.dirent)); + dir_fp->readdir_data.dirent = NULL; + break; + } + + de = (struct ksmbd_dirent *) + ((char *)dir_fp->readdir_data.dirent); + } else { + de = (struct ksmbd_dirent *) + ((char *)dir_fp->readdir_data.dirent + + dir_fp->dirent_offset); + } + + reclen = ALIGN(sizeof(struct ksmbd_dirent) + de->namelen, + sizeof(__le64)); + dir_fp->dirent_offset += reclen; + + if (dir_fp->readdir_data.file_attr & + SMB_SEARCH_ATTRIBUTE_DIRECTORY && de->d_type != DT_DIR) + continue; + + if (dir_fp->readdir_data.file_attr & + SMB_SEARCH_ATTRIBUTE_ARCHIVE && (de->d_type == DT_DIR || + (!strcmp(de->name, ".") || !strcmp(de->name, "..")))) + continue; + + ksmbd_kstat.kstat = &kstat; + + if (de->namelen > NAME_MAX) { + pr_err("filename length exceeds 255 bytes.\n"); + continue; + } + memcpy(d_info.smb1_name, de->name, de->namelen); + d_info.smb1_name[de->namelen] = '\0'; + d_info.name = (const char *)d_info.smb1_name; + d_info.name_len = de->namelen; + rc = ksmbd_vfs_readdir_name(work, + file_mnt_user_ns(dir_fp->filp), + &ksmbd_kstat, + de->name, + de->namelen, + dir_fp->filename); + if (rc) { + ksmbd_debug(SMB, "Err while dirent read rc = %d\n", rc); + rc = 0; + continue; + } + + if (ksmbd_share_veto_filename(share, d_info.name)) { + ksmbd_debug(SMB, "file(%s) is invisible by setting as veto file\n", + d_info.name); + continue; + } + + ksmbd_debug(SMB, "filename string = %.*s\n", + d_info.name_len, d_info.name); + rc = smb_populate_readdir_entry(conn, + le16_to_cpu(req_params->InformationLevel), &d_info, + &ksmbd_kstat); + if (rc == -ENOSPC) + break; + else if (rc) + goto err_out; + + } while (d_info.out_buf_len >= 0); + + if (d_info.out_buf_len < 0) + dir_fp->dirent_offset -= reclen; + + params = (struct smb_com_trans2_fnext_rsp_params *) + ((char *)rsp + sizeof(struct smb_com_trans_rsp)); + params->SearchCount = cpu_to_le16(d_info.num_entry); + + if (d_info.out_buf_len < 0) { + ksmbd_debug(SMB, "continue search\n"); + params->EndofSearch = cpu_to_le16(0); + params->LastNameOffset = cpu_to_le16(d_info.last_entry_offset); + } else { + ksmbd_debug(SMB, "end of search\n"); + params->EndofSearch = cpu_to_le16(1); + params->LastNameOffset = cpu_to_le16(0); + path_put(&(dir_fp->filp->f_path)); + if (le16_to_cpu(req_params->SearchFlags) & + CIFS_SEARCH_CLOSE_AT_END) + ksmbd_close_fd(work, sid); + } + params->EAErrorOffset = cpu_to_le16(0); + + rsp_hdr->WordCount = 0x0A; + rsp->t2.TotalParameterCount = cpu_to_le16(params_count); + rsp->t2.TotalDataCount = cpu_to_le16(d_info.data_count); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(params_count); + rsp->t2.ParameterOffset = + cpu_to_le16(sizeof(struct smb_com_trans_rsp) - 4); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(d_info.data_count); + rsp->t2.DataOffset = cpu_to_le16(sizeof(struct smb_com_trans_rsp) + + params_count + data_alignment_offset - 4); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + rsp->Pad = 0; + rsp->ByteCount = cpu_to_le16(d_info.data_count + params_count + 1 + + data_alignment_offset); + memset((char *)rsp + sizeof(struct smb_com_trans_rsp) + + params_count, '\0', data_alignment_offset); + inc_rfc1001_len(rsp_hdr, (10 * 2 + d_info.data_count + + params_count + 1 + data_alignment_offset)); + kfree(d_info.smb1_name); + ksmbd_fd_put(work, dir_fp); + return 0; + +err_out: + if (rc == -EINVAL) + rsp_hdr->Status.CifsError = STATUS_INVALID_PARAMETER; + else if (rc == -EACCES || rc == -EXDEV) + rsp_hdr->Status.CifsError = STATUS_ACCESS_DENIED; + else if (rc == -ENOENT) + rsp_hdr->Status.CifsError = STATUS_NO_SUCH_FILE; + else if (rc == -EBADF) + rsp_hdr->Status.CifsError = STATUS_FILE_CLOSED; + else if (rc == -ENOMEM) + rsp_hdr->Status.CifsError = STATUS_NO_MEMORY; + else if (rc == -EFAULT) + rsp_hdr->Status.CifsError = STATUS_INVALID_LEVEL; + if (!rsp->hdr.Status.CifsError) + rsp->hdr.Status.CifsError = STATUS_UNEXPECTED_IO_ERROR; + + if (dir_fp) { + if (dir_fp->readdir_data.dirent) { + free_page((unsigned long)(dir_fp->readdir_data.dirent)); + dir_fp->readdir_data.dirent = NULL; + } + path_put(&(dir_fp->filp->f_path)); + ksmbd_close_fd(work, sid); + } + + kfree(d_info.smb1_name); + return 0; +} + +/** + * smb_set_alloc_size() - set file truncate method using trans2 + * set file info command - file allocation info level + * @work: smb work containing set file info command buffer + * + * Return: 0 on success, otherwise error + */ +static int smb_set_alloc_size(struct ksmbd_work *work) +{ + struct smb_com_trans2_sfi_req *req; + struct smb_com_trans2_sfi_rsp *rsp; + struct file_allocation_info *allocinfo; + struct kstat stat; + struct ksmbd_file *fp = NULL; + loff_t newsize; + int err = 0; + + req = (struct smb_com_trans2_sfi_req *)work->request_buf; + rsp = (struct smb_com_trans2_sfi_rsp *)work->response_buf; + + allocinfo = (struct file_allocation_info *) + (((char *) &req->hdr.Protocol) + le16_to_cpu(req->DataOffset)); + newsize = le64_to_cpu(allocinfo->AllocationSize); + + fp = ksmbd_lookup_fd_fast(work, req->Fid); + if (!fp) { + pr_err("failed to get filp for fid %u\n", req->Fid); + rsp->hdr.Status.CifsError = STATUS_FILE_CLOSED; + return -ENOENT; + } + + err = ksmbd_vfs_getattr(&fp->filp->f_path, &stat); + if (err) { + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + ksmbd_fd_put(work, fp); + return err; + } + + if (newsize == stat.size) /* nothing to do */ + goto out; + + /* Round up size */ + if (alloc_roundup_size) { + newsize = div64_u64(newsize + alloc_roundup_size - 1, + alloc_roundup_size); + newsize *= alloc_roundup_size; + } + + err = ksmbd_vfs_truncate(work, fp, newsize); + if (err) { + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + ksmbd_fd_put(work, fp); + return err; + } + +out: + ksmbd_debug(SMB, "fid %u, truncated to newsize %llu\n", + req->Fid, newsize); + + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = 0; + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = rsp->t2.TotalParameterCount; + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = 0; + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + + /* 3 pad (1 pad1 + 2 pad2)*/ + rsp->ByteCount = cpu_to_le16(3); + rsp->Reserved2 = 0; + inc_rfc1001_len(&rsp->hdr, rsp->hdr.WordCount * 2 + 3); + ksmbd_fd_put(work, fp); + + return 0; +} + +/** + * smb_set_file_size_finfo() - set file truncate method using trans2 + * set file info command + * @work: smb work containing set file info command buffer + * + * Return: 0 on success, otherwise error + */ +static int smb_set_file_size_finfo(struct ksmbd_work *work) +{ + struct smb_com_trans2_sfi_req *req; + struct smb_com_trans2_sfi_rsp *rsp; + struct file_end_of_file_info *eofinfo; + struct ksmbd_file *fp; + loff_t newsize; + int err = 0; + + req = (struct smb_com_trans2_sfi_req *)work->request_buf; + rsp = (struct smb_com_trans2_sfi_rsp *)work->response_buf; + + eofinfo = (struct file_end_of_file_info *) + (((char *) &req->hdr.Protocol) + le16_to_cpu(req->DataOffset)); + + fp = ksmbd_lookup_fd_fast(work, req->Fid); + if (!fp) { + pr_err("failed to get filp for fid %u\n", req->Fid); + rsp->hdr.Status.CifsError = STATUS_FILE_CLOSED; + return -ENOENT; + } + + newsize = le64_to_cpu(eofinfo->FileSize); + err = ksmbd_vfs_truncate(work, fp, newsize); + if (err) { + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + ksmbd_fd_put(work, fp); + return err; + } + + ksmbd_debug(SMB, "fid %u, truncated to newsize %lld\n", req->Fid, + newsize); + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = 0; + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = rsp->t2.TotalParameterCount; + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = 0; + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + + /* 3 pad (1 pad1 + 2 pad2)*/ + rsp->ByteCount = cpu_to_le16(3); + rsp->Reserved2 = 0; + inc_rfc1001_len(&rsp->hdr, rsp->hdr.WordCount * 2 + 3); + ksmbd_fd_put(work, fp); + + return 0; +} + +/** + * query_file_info_pipe() - query file info of IPC pipe + * using query file info command + * @work: smb work containing query file info command buffer + * + * Return: 0 on success, otherwise error + */ +static int query_file_info_pipe(struct ksmbd_work *work) +{ + struct smb_hdr *rsp_hdr = work->response_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + struct smb_com_trans2_req *req = work->request_buf; + struct smb_trans2_qfi_req_params *req_params; + struct file_standard_info *standard_info; + char *ptr; + + req_params = (struct smb_trans2_qfi_req_params *)(work->request_buf + + le16_to_cpu(req->ParameterOffset) + 4); + + if (le16_to_cpu(req_params->InformationLevel) != + SMB_QUERY_FILE_STANDARD_INFO) { + ksmbd_debug(SMB, "query file info for info %u not supported\n", + le16_to_cpu(req_params->InformationLevel)); + rsp_hdr->Status.CifsError = STATUS_NOT_SUPPORTED; + return -EOPNOTSUPP; + } + + ksmbd_debug(SMB, "SMB_QUERY_FILE_STANDARD_INFO\n"); + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = cpu_to_le16(sizeof(struct file_standard_info)); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(sizeof(struct file_standard_info)); + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + /*2 for parameter count & 3 pad (1pad1 + 2 pad2)*/ + rsp->ByteCount = cpu_to_le16(2 + sizeof(struct file_standard_info) + 3); + rsp->Pad = 0; + /* lets set EA info */ + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + standard_info = (struct file_standard_info *)(ptr + 4); + standard_info->AllocationSize = cpu_to_le64(4096); + standard_info->EndOfFile = 0; + standard_info->NumberOfLinks = cpu_to_le32(1); + standard_info->DeletePending = 0; + standard_info->Directory = 0; + standard_info->DeletePending = 1; + inc_rfc1001_len(rsp_hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + + return 0; +} + +/** + * query_file_info() - query file info of file/dir + * using query file info command + * @work: smb work containing query file info command buffer + * + * Return: 0 on success, otherwise error + */ +static int query_file_info(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb_hdr *rsp_hdr = work->response_buf; + struct smb_com_trans2_req *req = work->request_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + struct smb_trans2_qfi_req_params *req_params; + struct ksmbd_file *fp; + struct kstat st; + char *ptr; + int rc = 0; + u64 time; + + req_params = (struct smb_trans2_qfi_req_params *)(work->request_buf + + le16_to_cpu(req->ParameterOffset) + 4); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "query file info for IPC srvsvc\n"); + return query_file_info_pipe(work); + } + + fp = ksmbd_lookup_fd_fast(work, req_params->Fid); + if (!fp) { + pr_err("failed to get filp for fid %u\n", req_params->Fid); + rsp_hdr->Status.CifsError = STATUS_UNEXPECTED_IO_ERROR; + rc = -EIO; + goto err_out; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), &st); +#else + generic_fillattr(file_inode(fp->filp), &st); +#endif + + switch (le16_to_cpu(req_params->InformationLevel)) { + + case SMB_QUERY_FILE_STANDARD_INFO: + { + struct file_standard_info *standard_info; + unsigned int delete_pending; + + ksmbd_debug(SMB, "SMB_QUERY_FILE_STANDARD_INFO\n"); + delete_pending = ksmbd_inode_pending_delete(fp); + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = + cpu_to_le16(sizeof(struct file_standard_info)); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = + cpu_to_le16(sizeof(struct file_standard_info)); + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + /*2 for parameter count & 3 pad (1pad1 + 2 pad2)*/ + rsp->ByteCount = + cpu_to_le16(2 + sizeof(struct file_standard_info) + 3); + rsp->Pad = 0; + /* lets set EA info */ + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + standard_info = (struct file_standard_info *)(ptr + 4); + standard_info->AllocationSize = cpu_to_le64(st.blocks << 9); + standard_info->EndOfFile = cpu_to_le64(st.size); + standard_info->NumberOfLinks = cpu_to_le32(get_nlink(&st) - + delete_pending); + standard_info->DeletePending = delete_pending; + standard_info->Directory = S_ISDIR(st.mode) ? 1 : 0; + inc_rfc1001_len(rsp_hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + break; + } + case SMB_QUERY_FILE_BASIC_INFO: + { + struct file_basic_info *basic_info; + + ksmbd_debug(SMB, "SMB_QUERY_FILE_BASIC_INFO\n"); + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = + cpu_to_le16(sizeof(struct file_basic_info)); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(sizeof(struct file_basic_info)); + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + /*2 for parameter count & 3 pad (1pad1 + 2 pad2)*/ + rsp->ByteCount = + cpu_to_le16(2 + sizeof(struct file_basic_info) + 3); + rsp->Pad = 0; + /* lets set EA info */ + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + basic_info = (struct file_basic_info *)(ptr + 4); + basic_info->CreationTime = + cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(st.atime); + basic_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(st.mtime); + basic_info->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(st.ctime); + basic_info->ChangeTime = cpu_to_le64(time); + basic_info->Attributes = S_ISDIR(st.mode) ? + ATTR_DIRECTORY_LE : ATTR_ARCHIVE_LE; + basic_info->Pad = 0; + inc_rfc1001_len(rsp_hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + break; + } + case SMB_QUERY_FILE_EA_INFO: + { + struct file_ea_info *ea_info; + + ksmbd_debug(SMB, "SMB_QUERY_FILE_EA_INFO\n"); + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = + cpu_to_le16(sizeof(struct file_ea_info)); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(sizeof(struct file_ea_info)); + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + /*2 for parameter count & 3 pad (1pad1 + 2 pad2)*/ + rsp->ByteCount = + cpu_to_le16(2 + sizeof(struct file_ea_info) + 3); + rsp->Pad = 0; + /* lets set EA info */ + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + ea_info = (struct file_ea_info *)(ptr + 4); + ea_info->EaSize = 0; + inc_rfc1001_len(rsp_hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + break; + } + case SMB_QUERY_FILE_UNIX_BASIC: + { + struct file_unix_basic_info *uinfo; + + ksmbd_debug(SMB, "SMB_QUERY_FILE_UNIX_BASIC\n"); + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = + cpu_to_le16(sizeof(struct file_unix_basic_info)); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = + cpu_to_le16(sizeof(struct file_unix_basic_info)); + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + /*2 for parameter count & 3 pad (1pad1 + 2 pad2)*/ + rsp->ByteCount = + cpu_to_le16(2 + sizeof(struct file_unix_basic_info) + + 3); + rsp->Pad = 0; + /* lets set unix info info */ + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + uinfo = (struct file_unix_basic_info *)(ptr + 4); + init_unix_info(uinfo, file_mnt_user_ns(fp->filp), &st); + inc_rfc1001_len(rsp_hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + break; + } + case SMB_QUERY_FILE_NAME_INFO: + { + struct file_name_info *name_info; + int uni_filename_len; + char *filename; + + ksmbd_debug(SMB, "SMB_QUERY_FILE_NAME_INFO\n"); + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + name_info = (struct file_name_info *)(ptr + 4); + + filename = convert_to_nt_pathname(work->tcon->share_conf, + &fp->filp->f_path); + if (!filename) { + rc = -ENOMEM; + goto err_out; + } + uni_filename_len = smbConvertToUTF16( + (__le16 *)name_info->FileName, + filename, PATH_MAX, + conn->local_nls, 0); + kfree(filename); + uni_filename_len *= 2; + name_info->FileNameLength = cpu_to_le32(uni_filename_len); + + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = cpu_to_le16(uni_filename_len + 4); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(uni_filename_len + 4); + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + /*2 for parameter count & 3 pad (1pad1 + 2 pad2)*/ + rsp->ByteCount = cpu_to_le16(2 + uni_filename_len + 4 + 3); + rsp->Pad = 0; + inc_rfc1001_len(rsp_hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + break; + } + case SMB_QUERY_FILE_ALL_INFO: + { + struct file_all_info *ainfo; + unsigned int delete_pending; + + ksmbd_debug(SMB, "SMB_QUERY_FILE_UNIX_BASIC\n"); + delete_pending = ksmbd_inode_pending_delete(fp); + rsp_hdr->WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = + cpu_to_le16(sizeof(struct file_all_info)); + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = cpu_to_le16(2); + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = cpu_to_le16(sizeof(struct file_all_info)); + rsp->t2.DataOffset = cpu_to_le16(60); + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + /*2 for parameter count & 3 pad (1pad1 + 2 pad2)*/ + rsp->ByteCount = + cpu_to_le16(2 + sizeof(struct file_all_info) + 3); + rsp->Pad = 0; + /* lets set all info info */ + ptr = (char *)&rsp->Pad + 1; + memset(ptr, 0, 4); + ainfo = (struct file_all_info *)(ptr + 4); + ainfo->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(st.atime); + ainfo->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(st.mtime); + ainfo->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(st.ctime); + ainfo->ChangeTime = cpu_to_le64(time); + ainfo->Attributes = cpu_to_le32(S_ISDIR(st.mode) ? + ATTR_DIRECTORY : ATTR_ARCHIVE); + ainfo->Pad1 = 0; + ainfo->AllocationSize = cpu_to_le64(st.blocks << 9); + ainfo->EndOfFile = cpu_to_le64(st.size); + ainfo->NumberOfLinks = cpu_to_le32(get_nlink(&st) - + delete_pending); + ainfo->DeletePending = delete_pending; + ainfo->Directory = S_ISDIR(st.mode) ? 1 : 0; + ainfo->Pad2 = 0; + ainfo->EASize = 0; + ainfo->FileNameLength = 0; + inc_rfc1001_len(rsp_hdr, 10 * 2 + le16_to_cpu(rsp->ByteCount)); + break; + } + default: + pr_err("query path info not implemnted for %x\n", + le16_to_cpu(req_params->InformationLevel)); + rsp_hdr->Status.CifsError = STATUS_NOT_SUPPORTED; + rc = -EINVAL; + goto err_out; + + } + +err_out: + ksmbd_fd_put(work, fp); + return rc; +} + +/** + * smb_set_unix_fileinfo() - set smb unix file info(setattr) + * @work: smb work containing unix basic info buffer + * + * Return: 0 on success, otherwise error + */ +static int smb_set_unix_fileinfo(struct ksmbd_work *work) +{ + struct smb_com_trans2_sfi_req *req = work->request_buf; + struct smb_com_trans2_sfi_rsp *rsp = work->response_buf; + struct file_unix_basic_info *unix_info; + struct ksmbd_file *fp; + struct iattr attrs; + int err = 0; + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + fp = ksmbd_lookup_fd_fast(work, req->Fid); + if (!fp) { + ksmbd_revert_fsids(work); + return -ENOENT; + } + + unix_info = (struct file_unix_basic_info *) + (((char *) &req->hdr.Protocol) + le16_to_cpu(req->DataOffset)); + + attrs.ia_valid = 0; + attrs.ia_mode = 0; + err = unix_info_to_attr(unix_info, + file_mnt_user_ns(fp->filp), &attrs); + ksmbd_fd_put(work, fp); + ksmbd_revert_fsids(work); + if (err) + goto out; + + err = ksmbd_vfs_setattr(work, NULL, (u64)req->Fid, &attrs); + if (err) + goto out; + + /* setattr success, prepare response */ + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = 0; + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = rsp->t2.TotalParameterCount; + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = 0; + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + + /* 3 pad (1 pad1 + 2 pad2)*/ + rsp->ByteCount = cpu_to_le16(3); + rsp->Reserved2 = 0; + inc_rfc1001_len(&rsp->hdr, + rsp->hdr.WordCount * 2 + le16_to_cpu(rsp->ByteCount)); + +out: + if (err) { + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + return err; + } + return 0; +} + +/** + * smb_set_dispostion() - set file dispostion method using trans2 + * using set file info command + * @work: smb work containing set file info command buffer + * + * Return: 0 on success, otherwise error + */ +static int smb_set_dispostion(struct ksmbd_work *work) +{ + struct smb_com_trans2_sfi_req *req = work->request_buf; + struct smb_com_trans2_sfi_rsp *rsp = work->response_buf; + char *disp_info; + struct ksmbd_file *fp; + int ret = 0; + + disp_info = (char *) (((char *) &req->hdr.Protocol) + + le16_to_cpu(req->DataOffset)); + + fp = ksmbd_lookup_fd_fast(work, req->Fid); + if (!fp) { + ksmbd_debug(SMB, "Invalid id for close: %d\n", req->Fid); + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + return -EINVAL; + } + + if (*disp_info) { + if (!fp->is_nt_open) { + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + ret = -EPERM; + goto err_out; + } + + if (!(file_inode(fp->filp)->i_mode & 0222)) { + rsp->hdr.Status.CifsError = STATUS_CANNOT_DELETE; + ret = -EPERM; + goto err_out; + } + + if (S_ISDIR(file_inode(fp->filp)->i_mode) && + ksmbd_vfs_empty_dir(fp) == -ENOTEMPTY) { + rsp->hdr.Status.CifsError = STATUS_DIRECTORY_NOT_EMPTY; + ret = -ENOTEMPTY; + goto err_out; + } + + ksmbd_set_inode_pending_delete(fp); + } else { + ksmbd_clear_inode_pending_delete(fp); + } + + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = 0; + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = rsp->t2.TotalParameterCount; + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = 0; + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + + /* 3 pad (1 pad1 + 2 pad2)*/ + rsp->ByteCount = cpu_to_le16(3); + rsp->Reserved2 = 0; + inc_rfc1001_len(&rsp->hdr, + rsp->hdr.WordCount * 2 + 3); + +err_out: + ksmbd_fd_put(work, fp); + return ret; +} + +/** + * smb_set_time_fileinfo() - set file time method using trans2 + * using set file info command + * @work: smb work containing set file info command buffer + * + * Return: 0 on success, otherwise error + */ +static int smb_set_time_fileinfo(struct ksmbd_work *work) +{ + struct smb_com_trans2_sfi_req *req; + struct smb_com_trans2_sfi_rsp *rsp; + struct file_basic_info *info; + struct iattr attrs; + int err = 0; + + req = (struct smb_com_trans2_sfi_req *)work->request_buf; + rsp = (struct smb_com_trans2_sfi_rsp *)work->response_buf; + + info = (struct file_basic_info *)(((char *) &req->hdr.Protocol) + + le16_to_cpu(req->DataOffset)); + + attrs.ia_valid = 0; + if (le64_to_cpu(info->LastAccessTime)) { + attrs.ia_atime = smb_NTtimeToUnix(info->LastAccessTime); + attrs.ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET); + } + + if (le64_to_cpu(info->ChangeTime)) { + attrs.ia_ctime = smb_NTtimeToUnix(info->ChangeTime); + attrs.ia_valid |= ATTR_CTIME; + } + + if (le64_to_cpu(info->LastWriteTime)) { + attrs.ia_mtime = smb_NTtimeToUnix(info->LastWriteTime); + attrs.ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET); + } + /* TODO: check dos mode and acl bits if req->Attributes nonzero */ + + if (!attrs.ia_valid) + goto done; + + err = ksmbd_vfs_setattr(work, NULL, (u64)req->Fid, &attrs); + if (err) { + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + return err; + } + +done: + ksmbd_debug(SMB, "fid %u, setattr done\n", req->Fid); + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = 0; + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = rsp->t2.TotalParameterCount; + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = 0; + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + + /* 3 pad (1 pad1 + 2 pad2)*/ + rsp->ByteCount = cpu_to_le16(3); + rsp->Reserved2 = 0; + inc_rfc1001_len(&rsp->hdr, + rsp->hdr.WordCount * 2 + 3); + + return 0; +} + +/** + * smb_fileinfo_rename() - rename method using trans2 set file info command + * @work: smb work containing set file info command buffer + * + * Return: 0 on success, otherwise error + */ +static int smb_fileinfo_rename(struct ksmbd_work *work) +{ + struct smb_com_trans2_sfi_req *req; + struct smb_com_trans2_sfi_rsp *rsp; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct set_file_rename *info; + struct ksmbd_file *fp; + char *newname; + int rc = 0; + + req = (struct smb_com_trans2_sfi_req *)work->request_buf; + rsp = (struct smb_com_trans2_sfi_rsp *)work->response_buf; + info = (struct set_file_rename *) + (((char *) &req->hdr.Protocol) + le16_to_cpu(req->DataOffset)); + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "returning as user does not have permission to write\n"); + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + return -EACCES; + } + + fp = ksmbd_lookup_fd_fast(work, req->Fid); + if (!fp) { + pr_err("failed to get filp for fid %u\n", req->Fid); + rsp->hdr.Status.CifsError = STATUS_FILE_CLOSED; + return -ENOENT; + } + + if (info->overwrite) { + rc = ksmbd_vfs_truncate(work, fp, 0); + if (rc) { + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + ksmbd_fd_put(work, fp); + return rc; + } + } + + newname = smb_get_name(share, info->target_name, PATH_MAX, work, 0); + if (IS_ERR(newname)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + ksmbd_fd_put(work, fp); + return PTR_ERR(newname); + } + + ksmbd_debug(SMB, "new name(%s)\n", newname); + rc = ksmbd_vfs_fp_rename(work, fp, newname); + if (rc) { + rsp->hdr.Status.CifsError = STATUS_UNEXPECTED_IO_ERROR; + goto out; + } + + rsp->hdr.WordCount = 10; + rsp->t2.TotalParameterCount = cpu_to_le16(2); + rsp->t2.TotalDataCount = 0; + rsp->t2.Reserved = 0; + rsp->t2.ParameterCount = rsp->t2.TotalParameterCount; + rsp->t2.ParameterOffset = cpu_to_le16(56); + rsp->t2.ParameterDisplacement = 0; + rsp->t2.DataCount = rsp->t2.TotalDataCount; + rsp->t2.DataOffset = 0; + rsp->t2.DataDisplacement = 0; + rsp->t2.SetupCount = 0; + rsp->t2.Reserved1 = 0; + + /* 3 pad (1 pad1 + 2 pad2)*/ + rsp->ByteCount = cpu_to_le16(3); + rsp->Reserved2 = 0; + inc_rfc1001_len(&rsp->hdr, rsp->hdr.WordCount * 2 + 3); + +out: + ksmbd_fd_put(work, fp); + kfree(newname); + return rc; +} + +/** + * set_file_info() - trans2 set file info command dispatcher + * @work: smb work containing set file info command buffer + * + * Return: 0 on success, otherwise error + */ +static int set_file_info(struct ksmbd_work *work) +{ + struct smb_com_trans2_sfi_req *req; + struct smb_com_trans2_sfi_rsp *rsp; + __u16 info_level, total_param; + int err = 0; + + req = (struct smb_com_trans2_sfi_req *)work->request_buf; + rsp = (struct smb_com_trans2_sfi_rsp *)work->response_buf; + info_level = le16_to_cpu(req->InformationLevel); + total_param = le16_to_cpu(req->TotalParameterCount); + if (total_param < 4) { + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + pr_err("invalid total parameter for info_level 0x%x\n", + total_param); + return -EINVAL; + } + + switch (info_level) { + case SMB_SET_FILE_EA: + err = smb_set_ea(work); + break; + case SMB_SET_FILE_ALLOCATION_INFO2: + /* fall through */ + case SMB_SET_FILE_ALLOCATION_INFO: + err = smb_set_alloc_size(work); + break; + case SMB_SET_FILE_END_OF_FILE_INFO2: + /* fall through */ + case SMB_SET_FILE_END_OF_FILE_INFO: + err = smb_set_file_size_finfo(work); + break; + case SMB_SET_FILE_UNIX_BASIC: + err = smb_set_unix_fileinfo(work); + break; + case SMB_SET_FILE_DISPOSITION_INFO: + case SMB_SET_FILE_DISPOSITION_INFORMATION: + err = smb_set_dispostion(work); + break; + case SMB_SET_FILE_BASIC_INFO2: + /* fall through */ + case SMB_SET_FILE_BASIC_INFO: + err = smb_set_time_fileinfo(work); + break; + case SMB_SET_FILE_RENAME_INFORMATION: + err = smb_fileinfo_rename(work); + break; + default: + ksmbd_debug(SMB, "info level = %x not implemented yet\n", + info_level); + rsp->hdr.Status.CifsError = STATUS_NOT_IMPLEMENTED; + return -EOPNOTSUPP; + } + + if (err < 0) + ksmbd_debug(SMB, "info_level 0x%x failed, err %d\n", + info_level, err); + return err; +} + +/** + * create_dir() - trans2 create directory dispatcher + * @work: smb work containing set file info command buffer + * + * Return: 0 on success, otherwise error + */ +static int create_dir(struct ksmbd_work *work) +{ + struct smb_com_trans2_req *req = work->request_buf; + struct smb_com_trans2_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + mode_t mode = S_IALLUGO; + char *name; + int err; + + name = smb_get_name(share, work->request_buf + + le16_to_cpu(req->ParameterOffset) + 4, + PATH_MAX, work, false); + if (IS_ERR(name)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(name); + } + + if (ksmbd_override_fsids(work)) { + kfree(name); + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + return -ENOMEM; + } + + err = ksmbd_vfs_mkdir(work, name, mode); + if (err) { + if (err == -EEXIST) { + if (!(((struct smb_hdr *)work->request_buf)->Flags2 & + SMBFLG2_ERR_STATUS)) { + ntstatus_to_dos(STATUS_OBJECT_NAME_COLLISION, + &rsp->hdr.Status.DosError.ErrorClass, + &rsp->hdr.Status.DosError.Error); + } else + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_COLLISION; + } else + rsp->hdr.Status.CifsError = STATUS_DATA_ERROR; + goto out; + } else + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + __u64 ctime; + struct path path; + struct xattr_dos_attrib da = {0}; + + err = ksmbd_vfs_kern_path(work, name, 0, &path, 1); + if (!err) { + ctime = ksmbd_UnixTimeToNT(current_time(d_inode(path.dentry))); + + da.version = 4; + da.attr = ATTR_DIRECTORY; + da.itime = da.create_time = ctime; + da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | + XATTR_DOSINFO_ITIME; + + err = ksmbd_vfs_set_dos_attrib_xattr(mnt_user_ns(path.mnt), + path.dentry, &da); + if (err) + ksmbd_debug(SMB, "failed to store creation time in EA\n"); + path_put(&path); + } + err = 0; + } + +out: + memset(&rsp->hdr.WordCount, 0, 3); + ksmbd_revert_fsids(work); + kfree(name); + return err; +} + +/** + * get_dfs_referral() - handler for smb dfs referral command + * @work: smb work containing get dfs referral command buffer + * + * Return: 0 on success, otherwise error + */ +static int get_dfs_referral(struct ksmbd_work *work) +{ + struct smb_hdr *rsp_hdr = (struct smb_hdr *)work->response_buf; + + rsp_hdr->Status.CifsError = STATUS_NOT_SUPPORTED; + return 0; +} + +/** + * smb_trans2() - handler for trans2 commands + * @work: smb work containing trans2 command buffer + * + * Return: 0 on success, otherwise error + */ +int smb_trans2(struct ksmbd_work *work) +{ + struct smb_com_trans2_req *req = work->request_buf; + struct smb_hdr *rsp_hdr = work->response_buf; + int err = 0; + u16 sub_command = le16_to_cpu(req->SubCommand); + + /* at least one setup word for TRANS2 command + * MS-CIFS, SMB COM TRANSACTION + */ + if (req->SetupCount < 1) { + pr_err("Wrong setup count in SMB_TRANS2 - indicates wrong request\n"); + rsp_hdr->Status.CifsError = STATUS_UNSUCCESSFUL; + return -EINVAL; + } + + switch (sub_command) { + case TRANS2_FIND_FIRST: + err = find_first(work); + break; + case TRANS2_FIND_NEXT: + err = find_next(work); + break; + case TRANS2_QUERY_FS_INFORMATION: + err = query_fs_info(work); + break; + case TRANS2_QUERY_PATH_INFORMATION: + err = query_path_info(work); + break; + case TRANS2_SET_PATH_INFORMATION: + err = set_path_info(work); + break; + case TRANS2_SET_FS_INFORMATION: + err = set_fs_info(work); + break; + case TRANS2_QUERY_FILE_INFORMATION: + err = query_file_info(work); + break; + case TRANS2_SET_FILE_INFORMATION: + err = set_file_info(work); + break; + case TRANS2_CREATE_DIRECTORY: + err = create_dir(work); + break; + case TRANS2_GET_DFS_REFERRAL: + err = get_dfs_referral(work); + break; + default: + ksmbd_debug(SMB, "sub command 0x%x not implemented yet\n", + sub_command); + rsp_hdr->Status.CifsError = STATUS_NOT_SUPPORTED; + return -EINVAL; + } + + if (err) { + ksmbd_debug(SMB, "%s failed with error %d\n", __func__, err); + if (err == -EBUSY) + rsp_hdr->Status.CifsError = STATUS_DELETE_PENDING; + return err; + } + + return 0; +} + +/** + * smb_mkdir() - handler for smb mkdir + * @work: smb work containing creat directory command buffer + * + * Return: 0 on success, otherwise error + */ +int smb_mkdir(struct ksmbd_work *work) +{ + struct smb_com_create_directory_req *req = work->request_buf; + struct smb_com_create_directory_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + mode_t mode = S_IALLUGO; + char *name; + int err; + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "returning as user does not have permission to write\n"); + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + return -EACCES; + } + + name = smb_get_name(share, req->DirName, PATH_MAX, work, false); + if (IS_ERR(name)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(name); + } + + if (ksmbd_override_fsids(work)) { + kfree(name); + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + return -ENOMEM; + } + + err = ksmbd_vfs_mkdir(work, name, mode); + if (err) { + if (err == -EEXIST) { + if (!(((struct smb_hdr *)work->request_buf)->Flags2 & + SMBFLG2_ERR_STATUS)) { + rsp->hdr.Status.DosError.ErrorClass = ERRDOS; + rsp->hdr.Status.DosError.Error = + cpu_to_le16(ERRnoaccess); + } else + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_COLLISION; + } else + rsp->hdr.Status.CifsError = STATUS_DATA_ERROR; + goto out; + } else { + /* mkdir success, return response to server */ + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 0; + rsp->ByteCount = 0; + } + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + __u64 ctime; + struct path path; + struct xattr_dos_attrib da = {0}; + + err = ksmbd_vfs_kern_path(work, name, 0, &path, 1); + if (!err) { + ctime = ksmbd_UnixTimeToNT(current_time(d_inode(path.dentry))); + + da.version = 4; + da.attr = ATTR_DIRECTORY; + da.itime = da.create_time = ctime; + da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | + XATTR_DOSINFO_ITIME; + + err = ksmbd_vfs_set_dos_attrib_xattr(mnt_user_ns(path.mnt), + path.dentry, &da); + if (err) + ksmbd_debug(SMB, "failed to store creation time in xattr\n"); + path_put(&path); + } + err = 0; + } + +out: + ksmbd_revert_fsids(work); + kfree(name); + return err; +} + +/** + * smb_checkdir() - handler to verify whether a specified + * path resolves to a valid directory or not + * + * @work: smb work containing creat directory command buffer + * + * Return: 0 on success, otherwise error + */ +int smb_checkdir(struct ksmbd_work *work) +{ + struct smb_com_check_directory_req *req = work->request_buf; + struct smb_com_check_directory_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct path path; + struct kstat stat; + char *name, *last; + int err; + bool caseless_lookup = req->hdr.Flags & SMBFLG_CASELESS; + + name = smb_get_name(share, req->DirName, PATH_MAX, work, false); + if (IS_ERR(name)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(name); + } + + err = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, + caseless_lookup); + if (err) { + if (err == -ENOENT) { + /* + * If the parent directory is valid but not the + * last component - then returns + * STATUS_OBJECT_NAME_NOT_FOUND + * for that case and STATUS_OBJECT_PATH_NOT_FOUND + * if the path is invalid. + */ + last = strrchr(name, '/'); + if (last && last[1] != '\0') { + *last = '\0'; + last++; + + err = ksmbd_vfs_kern_path(work, name, LOOKUP_FOLLOW | + LOOKUP_DIRECTORY, &path, + caseless_lookup); + } else { + ksmbd_debug(SMB, "can't lookup parent %s\n", + name); + err = -ENOENT; + } + } + if (err) { + ksmbd_debug(SMB, "look up failed err %d\n", err); + switch (err) { + case -ENOENT: + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_NOT_FOUND; + break; + case -ENOMEM: + rsp->hdr.Status.CifsError = + STATUS_INSUFFICIENT_RESOURCES; + break; + case -EACCES: + rsp->hdr.Status.CifsError = + STATUS_ACCESS_DENIED; + break; + case -EIO: + rsp->hdr.Status.CifsError = + STATUS_DATA_ERROR; + break; + default: + rsp->hdr.Status.CifsError = + STATUS_OBJECT_PATH_SYNTAX_BAD; + break; + } + kfree(name); + return err; + } + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(mnt_user_ns(path.mnt), d_inode(path.dentry), &stat); +#else + generic_fillattr(d_inode(path.dentry), &stat); +#endif + + if (!S_ISDIR(stat.mode)) { + rsp->hdr.Status.CifsError = STATUS_NOT_A_DIRECTORY; + } else { + /* checkdir success, return response to server */ + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 0; + rsp->ByteCount = 0; + } + + path_put(&path); + kfree(name); + return err; +} + +/** + * smb_process_exit() - handler for smb process exit + * @work: smb work containing process exit command buffer + * + * Return: 0 on success always + * This command is obsolete now. Starting with the LAN Manager 1.0 dialect, + * FIDs are no longer associated with PIDs.CIFS clients SHOULD NOT send + * SMB_COM_PROCESS_EXIT requests. Instead, CIFS clients SHOULD perform all + * process cleanup operations, sending individual file close operations + * as needed.Here it is implemented very minimally for sake + * of passing smbtorture testcases. + */ +int smb_process_exit(struct ksmbd_work *work) +{ + struct smb_com_process_exit_rsp *rsp = work->response_buf; + + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 0; + rsp->ByteCount = 0; + return 0; +} + +/** + * smb_rmdir() - handler for smb rmdir + * @work: smb work containing delete directory command buffer + * + * Return: 0 on success, otherwise error + */ +int smb_rmdir(struct ksmbd_work *work) +{ + struct smb_com_delete_directory_req *req = work->request_buf; + struct smb_com_delete_directory_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + char *name; + int err; + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "returning as user does not have permission to write\n"); + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + return -EACCES; + } + + name = smb_get_name(share, req->DirName, PATH_MAX, work, false); + if (IS_ERR(name)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(name); + } + + err = ksmbd_vfs_remove_file(work, name); + if (err) { + if (err == -ENOTEMPTY) + rsp->hdr.Status.CifsError = + STATUS_DIRECTORY_NOT_EMPTY; + else if (err == -ENOENT) + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_NOT_FOUND; + else + rsp->hdr.Status.CifsError = STATUS_DATA_ERROR; + } else { + /* rmdir success, return response to server */ + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 0; + rsp->ByteCount = 0; + } + + kfree(name); + return err; +} + +/** + * smb_unlink() - handler for smb delete file + * @work: smb work containing delete file command buffer + * + * Return: 0 on success, otherwise error + */ +int smb_unlink(struct ksmbd_work *work) +{ + struct smb_com_delete_file_req *req = work->request_buf; + struct smb_com_delete_file_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + char *name; + int err; + struct ksmbd_file *fp; + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "returning as user does not have permission to write\n"); + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + return -EACCES; + } + + name = smb_get_name(share, req->fileName, PATH_MAX, work, false); + if (IS_ERR(name)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(name); + } + + fp = ksmbd_lookup_fd_filename(work, name); + if (fp) + err = -ESHARE; + else + err = ksmbd_vfs_remove_file(work, name); + + if (err) { + if (err == -EISDIR) + rsp->hdr.Status.CifsError = + STATUS_FILE_IS_A_DIRECTORY; + else if (err == -ESHARE) + rsp->hdr.Status.CifsError = STATUS_SHARING_VIOLATION; + else if (err == -EACCES || err == -EXDEV) + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + else + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_NOT_FOUND; + } else { + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 0; + rsp->ByteCount = 0; + } + + ksmbd_fd_put(work, fp); + kfree(name); + return err; +} + +/** + * smb_nt_cancel() - handler for smb cancel command + * @work: smb work containing cancel command buffer + * + * Return: 0 + */ +int smb_nt_cancel(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb_hdr *hdr = (struct smb_hdr *)work->request_buf; + struct smb_hdr *work_hdr; + struct ksmbd_work *new_work; + + ksmbd_debug(SMB, "smb cancel called on mid %u\n", hdr->Mid); + + spin_lock(&conn->request_lock); + list_for_each_entry(new_work, &conn->requests, request_entry) { + work_hdr = (struct smb_hdr *)new_work->request_buf; + if (work_hdr->Mid == hdr->Mid) { + ksmbd_debug(SMB, "smb with mid %u cancelled command = 0x%x\n", + hdr->Mid, work_hdr->Command); + new_work->send_no_response = 1; + list_del_init(&new_work->request_entry); + new_work->sess->sequence_number--; + break; + } + } + spin_unlock(&conn->request_lock); + + /* For SMB_COM_NT_CANCEL command itself send no response */ + work->send_no_response = 1; + return 0; +} + +/** + * smb_nt_rename() - handler for smb rename command + * @work: smb work containing nt rename command buffer + * + * Return: 0 on success, otherwise error + */ +int smb_nt_rename(struct ksmbd_work *work) +{ + struct smb_com_nt_rename_req *req = work->request_buf; + struct smb_com_rename_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + char *oldname, *newname; + int oldname_len, err; + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "returning as user does not have permission to write\n"); + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + return -EACCES; + } + + if (le16_to_cpu(req->Flags) != CREATE_HARD_LINK) { + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + return -EINVAL; + } + + oldname = smb_get_name(share, req->OldFileName, PATH_MAX, work, false); + if (IS_ERR(oldname)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(oldname); + } + + if (is_smbreq_unicode(&req->hdr)) + oldname_len = smb1_utf16_name_length((__le16 *)req->OldFileName, + PATH_MAX); + else { + oldname_len = strlen(oldname); + oldname_len++; + } + + newname = smb_get_name(share, &req->OldFileName[oldname_len + 2], + PATH_MAX, work, false); + if (IS_ERR(newname)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + kfree(oldname); + return PTR_ERR(newname); + } + ksmbd_debug(SMB, "oldname %s, newname %s, oldname_len %d, unicode %d\n", + oldname, newname, oldname_len, + is_smbreq_unicode(&req->hdr)); + + err = ksmbd_vfs_link(work, oldname, newname); + if (err == -EACCES) + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + else if (err < 0) + rsp->hdr.Status.CifsError = STATUS_NOT_SAME_DEVICE; + + kfree(newname); + kfree(oldname); + return err; +} + +static __le32 smb_query_info_pipe(struct ksmbd_share_config *share, + struct kstat *st) +{ + st->mode = S_IFDIR; + return 0; +} + +static __le32 smb_query_info_path(struct ksmbd_work *work, struct kstat *st) +{ + struct smb_com_query_information_req *req = work->request_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct path path; + char *name; + __le32 err = 0; + int ret; + + name = smb_get_name(share, req->FileName, PATH_MAX, work, false); + if (IS_ERR(name)) + return STATUS_OBJECT_NAME_INVALID; + + if (ksmbd_override_fsids(work)) { + kfree(name); + return STATUS_NO_MEMORY; + } + + ret = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, 0); + if (ret) { + pr_err("look up failed err %d\n", ret); + + if (d_is_symlink(path.dentry)) { + err = STATUS_ACCESS_DENIED; + goto out; + } + err = STATUS_OBJECT_NAME_NOT_FOUND; + goto out; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(mnt_user_ns(path.mnt), d_inode(path.dentry), st); +#else + generic_fillattr(d_inode(path.dentry), st); +#endif + path_put(&path); +out: + ksmbd_revert_fsids(work); + kfree(name); + return err; +} + +/** + * smb_query_info() - handler for query information command + * @work: smb work containing query info command buffer + * + * Return: 0 on success, otherwise error + */ +int smb_query_info(struct ksmbd_work *work) +{ + struct smb_com_query_information_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct kstat st = {0,}; + __u16 attr = 0; + int i; + __le32 err; + + if (!test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) + err = smb_query_info_path(work, &st); + else + err = smb_query_info_pipe(share, &st); + + if (le32_to_cpu(err) != 0) { + rsp->hdr.Status.CifsError = err; + return -EINVAL; + } + + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 10; + + if (st.mode & S_ISVTX) + attr |= (ATTR_HIDDEN | ATTR_SYSTEM); + if (!(st.mode & 0222)) + attr |= ATTR_READONLY; + if (S_ISDIR(st.mode)) + attr |= ATTR_DIRECTORY; + + rsp->attr = cpu_to_le16(attr); + rsp->last_write_time = cpu_to_le32(st.mtime.tv_sec); + rsp->size = cpu_to_le32((u32)st.size); + for (i = 0; i < 5; i++) + rsp->reserved[i] = 0; + + rsp->ByteCount = 0; + inc_rfc1001_len(&rsp->hdr, rsp->hdr.WordCount * 2); + return 0; +} + +/** + * smb_closedir() - handler closing dir handle, opened for readdir + * @work: smb work containing find close command buffer + * + * Return: 0 on success, otherwise error + */ +int smb_closedir(struct ksmbd_work *work) +{ + struct smb_com_findclose_req *req = work->request_buf; + struct smb_com_close_rsp *rsp = work->response_buf; + int err; + + ksmbd_debug(SMB, "SMB_COM_FIND_CLOSE2 called for fid %u\n", + req->FileID); + + rsp->hdr.WordCount = 0; + rsp->ByteCount = 0; + + err = ksmbd_close_fd(work, req->FileID); + if (!err) + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + else + rsp->hdr.Status.CifsError = STATUS_INVALID_HANDLE; + return err; +} + +/** + * convert_open_flags() - convert smb open flags to file open flags + * @file_present: is file already present + * @mode: smp file open mode + * @disposition: smp file disposition information + * + * Return: converted file open flags + */ +static int convert_open_flags(bool file_present, + __u16 mode, __u16 dispostion, + int *may_flags) +{ + int oflags = 0; + + switch (mode & 0x0007) { + case SMBOPEN_READ: + oflags |= O_RDONLY; + break; + case SMBOPEN_WRITE: + oflags |= O_WRONLY; + break; + case SMBOPEN_READWRITE: + oflags |= O_RDWR; + break; + default: + oflags |= O_RDONLY; + break; + } + + if (mode & SMBOPEN_WRITE_THROUGH) + oflags |= O_SYNC; + + if (file_present) { + switch (dispostion & 0x0003) { + case SMBOPEN_DISPOSITION_NONE: + return -EEXIST; + case SMBOPEN_OAPPEND: + oflags |= O_APPEND; + break; + case SMBOPEN_OTRUNC: + oflags |= O_TRUNC; + break; + default: + break; + } + } else { + switch (dispostion & 0x0010) { + case SMBOPEN_DISPOSITION_NONE: + return -EINVAL; + case SMBOPEN_OCREATE: + oflags |= O_CREAT; + break; + default: + break; + } + } + + *may_flags = ksmbd_openflags_to_mayflags(oflags); + + return oflags; +} + +/** + * smb_open_andx() - smb andx open method handler + * @work: smb work containing buffer for andx open command buffer + * + * Return: error if there is error while processing current command, + * otherwise pointer to next andx command in the chain + */ +int smb_open_andx(struct ksmbd_work *work) +{ + struct smb_com_openx_req *req = work->request_buf; + struct smb_com_openx_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct path path; + struct kstat stat; + int oplock_flags, file_info, open_flags, may_flags; + char *name; + bool file_present = true; + umode_t mode = 0; + int err; + struct ksmbd_file *fp = NULL; + int oplock_rsp = OPLOCK_NONE, share_ret; + + rsp->hdr.Status.CifsError = STATUS_UNSUCCESSFUL; + + /* check for sharing mode flag */ + if ((le16_to_cpu(req->Mode) & SMBOPEN_SHARING_MODE) > + SMBOPEN_DENY_NONE) { + rsp->hdr.Status.DosError.ErrorClass = ERRDOS; + rsp->hdr.Status.DosError.Error = cpu_to_le16(ERRbadaccess); + rsp->hdr.Flags2 &= ~SMBFLG2_ERR_STATUS; + + memset(&rsp->hdr.WordCount, 0, 3); + return -EINVAL; + } + + if (is_smbreq_unicode(&req->hdr)) + name = smb_get_name(share, req->fileName + 1, PATH_MAX, + work, false); + else + name = smb_get_name(share, req->fileName, PATH_MAX, + work, false); + + if (IS_ERR(name)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(name); + } + + if (ksmbd_override_fsids(work)) { + kfree(name); + rsp->hdr.Status.CifsError = STATUS_NO_MEMORY; + return -ENOMEM; + } + + err = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, + req->hdr.Flags & SMBFLG_CASELESS); + if (err) { + if (err == -EACCES || err == -EXDEV) + goto out; + file_present = false; + } else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(mnt_user_ns(path.mnt), d_inode(path.dentry), &stat); +#else + generic_fillattr(d_inode(path.dentry), &stat); +#endif + + oplock_flags = le16_to_cpu(req->OpenFlags) & + (REQ_OPLOCK | REQ_BATCHOPLOCK); + + open_flags = convert_open_flags(file_present, + le16_to_cpu(req->Mode), + le16_to_cpu(req->OpenFunction), + &may_flags); + if (open_flags < 0) { + ksmbd_debug(SMB, "create_dispostion returned %d\n", open_flags); + if (file_present) + goto free_path; + else { + err = -ENOENT; + goto out; + } + } + + if (file_present && !(stat.mode & 0222)) { + if ((open_flags & O_ACCMODE) == O_WRONLY || + (open_flags & O_ACCMODE) == O_RDWR) { + ksmbd_debug(SMB, "readonly file(%s)\n", name); + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + memset(&rsp->hdr.WordCount, 0, 3); + goto free_path; + } + } + + if (!file_present && (open_flags & O_CREAT)) { + mode |= 0777; + if (le16_to_cpu(req->FileAttributes) & ATTR_READONLY) + mode &= ~0222; + + mode |= S_IFREG; + err = ksmbd_vfs_create(work, name, mode); + if (err) + goto out; + + err = ksmbd_vfs_kern_path(work, name, 0, &path, 0); + if (err) { + pr_err("cannot get linux path, err = %d\n", err); + goto out; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(mnt_user_ns(path.mnt), d_inode(path.dentry), &stat); +#else + generic_fillattr(d_inode(path.dentry), &stat); +#endif + } else if (file_present) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + err = inode_permission(mnt_user_ns(path.mnt), + d_inode(path.dentry), + may_flags); +#else + err = inode_permission(d_inode(path.dentry), + may_flags); +#endif + if (err) + goto free_path; + } + + err = ksmbd_query_inode_status(d_inode(path.dentry->d_parent)); + if (err == KSMBD_INODE_STATUS_PENDING_DELETE) { + err = -EBUSY; + goto free_path; + } + + err = 0; + ksmbd_debug(SMB, "(%s) open_flags = 0x%x, oplock_flags 0x%x\n", + name, open_flags, oplock_flags); + /* open file and get FID */ + fp = ksmbd_vfs_dentry_open(work, &path, open_flags, + 0, file_present); + if (IS_ERR(fp)) { + err = PTR_ERR(fp); + fp = NULL; + goto free_path; + } + fp->pid = le16_to_cpu(req->hdr.Pid); + + write_lock(&fp->f_ci->m_lock); + list_add(&fp->node, &fp->f_ci->m_fp_list); + write_unlock(&fp->f_ci->m_lock); + + share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp); + if (smb1_oplock_enable && + test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_OPLOCKS) && + !S_ISDIR(file_inode(fp->filp)->i_mode) && + oplock_flags) { + /* Client cannot request levelII oplock directly */ + err = smb_grant_oplock(work, oplock_flags, fp->volatile_id, + fp, le16_to_cpu(req->hdr.Tid), NULL, 0); + if (err) + goto free_path; + } else { + if (ksmbd_inode_pending_delete(fp)) { + err = -EBUSY; + goto free_path; + } + + if (share_ret < 0) { + err = -EPERM; + goto free_path; + } + } + + oplock_rsp = fp->f_opinfo != NULL ? fp->f_opinfo->level : 0; + + /* open success, send back response */ + if (file_present) { + if (!(open_flags & O_TRUNC)) + file_info = F_OPENED; + else + file_info = F_OVERWRITTEN; + } else + file_info = F_CREATED; + + if (oplock_rsp) + file_info |= SMBOPEN_LOCK_GRANTED; + + if (stat.result_mask & STATX_BTIME) + fp->create_time = ksmbd_UnixTimeToNT(stat.btime); + else + fp->create_time = ksmbd_UnixTimeToNT(stat.ctime); + if (file_present) { + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + struct xattr_dos_attrib da; + + err = ksmbd_vfs_get_dos_attrib_xattr(mnt_user_ns(path.mnt), + path.dentry, &da); + if (err > 0) { + fp->create_time = da.create_time; + fp->itime = da.itime; + } + err = 0; + } + } else { + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + struct xattr_dos_attrib da = {0}; + + da.version = 4; + da.attr = ATTR_NORMAL; + da.itime = da.create_time = fp->create_time; + da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | + XATTR_DOSINFO_ITIME; + + err = ksmbd_vfs_set_dos_attrib_xattr(mnt_user_ns(path.mnt), + path.dentry, &da); + if (err) + ksmbd_debug(SMB, "failed to store creation time in xattr\n"); + err = 0; + } + } + + /* prepare response buffer */ + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 0x0F; + rsp->Fid = fp->volatile_id; + rsp->FileAttributes = cpu_to_le16(ATTR_NORMAL); + rsp->LastWriteTime = cpu_to_le32(stat.mtime.tv_sec); + rsp->EndOfFile = cpu_to_le32(stat.size); + switch (open_flags & O_ACCMODE) { + case O_RDONLY: + rsp->Access = cpu_to_le16(SMB_DA_ACCESS_READ); + break; + case O_WRONLY: + rsp->Access = cpu_to_le16(SMB_DA_ACCESS_WRITE); + break; + case O_RDWR: + rsp->Access = cpu_to_le16(SMB_DA_ACCESS_READ_WRITE); + break; + default: + rsp->Access = cpu_to_le16(SMB_DA_ACCESS_READ); + break; + } + + rsp->FileType = 0; + rsp->IPCState = 0; + rsp->Action = cpu_to_le16(file_info); + rsp->Reserved = 0; + rsp->ByteCount = 0; + inc_rfc1001_len(&rsp->hdr, rsp->hdr.WordCount * 2); + +free_path: + path_put(&path); +out: + ksmbd_revert_fsids(work); + if (err) { + if (err == -ENOSPC) + rsp->hdr.Status.CifsError = STATUS_DISK_FULL; + else if (err == -EMFILE) + rsp->hdr.Status.CifsError = + STATUS_TOO_MANY_OPENED_FILES; + else if (err == -EBUSY) + rsp->hdr.Status.CifsError = STATUS_DELETE_PENDING; + else if (err == -ENOENT) + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_NOT_FOUND; + else if (err == -EACCES || err == -EXDEV) + rsp->hdr.Status.CifsError = STATUS_ACCESS_DENIED; + else + rsp->hdr.Status.CifsError = + STATUS_UNEXPECTED_IO_ERROR; + if (fp) + ksmbd_close_fd(work, fp->volatile_id); + } + + kfree(name); + if (!rsp->hdr.WordCount) + return err; + + /* this is an ANDx command ? */ + rsp->AndXReserved = 0; + rsp->AndXOffset = cpu_to_le16(get_rfc1002_len(&rsp->hdr)); + if (req->AndXCommand != SMB_NO_MORE_ANDX_COMMAND) { + /* adjust response */ + rsp->AndXCommand = req->AndXCommand; + return rsp->AndXCommand; /* More processing required */ + } + rsp->AndXCommand = SMB_NO_MORE_ANDX_COMMAND; + + return err; +} + +/** + * smb_setattr() - set file attributes + * @work: smb work containing setattr command + * + * Return: 0 on success, otherwise error + */ +int smb_setattr(struct ksmbd_work *work) +{ + struct smb_com_setattr_req *req = work->request_buf; + struct smb_com_setattr_rsp *rsp = work->response_buf; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct path path; + struct kstat stat; + struct iattr attrs; + int err = 0; + char *name; + __u16 dos_attr; + + name = smb_get_name(share, req->fileName, PATH_MAX, work, false); + if (IS_ERR(name)) { + rsp->hdr.Status.CifsError = + STATUS_OBJECT_NAME_INVALID; + return PTR_ERR(name); + } + + err = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, + req->hdr.Flags & SMBFLG_CASELESS); + if (err) { + ksmbd_debug(SMB, "look up failed err %d\n", err); + rsp->hdr.Status.CifsError = STATUS_OBJECT_NAME_NOT_FOUND; + err = 0; + goto out; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(mnt_user_ns(path.mnt), d_inode(path.dentry), &stat); +#else + generic_fillattr(d_inode(path.dentry), &stat); +#endif + path_put(&path); + attrs.ia_valid = 0; + attrs.ia_mode = 0; + + dos_attr = le16_to_cpu(req->attr); + if (!dos_attr) + attrs.ia_mode = stat.mode | 0200; + + if (dos_attr & ATTR_READONLY) + attrs.ia_mode = stat.mode & ~0222; + + if (attrs.ia_mode) + attrs.ia_valid |= ATTR_MODE; + + attrs.ia_mtime.tv_sec = le32_to_cpu(req->LastWriteTime); + attrs.ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET); + + err = ksmbd_vfs_setattr(work, name, 0, &attrs); + if (err) + goto out; + + rsp->hdr.Status.CifsError = STATUS_SUCCESS; + rsp->hdr.WordCount = 0; + rsp->ByteCount = 0; + +out: + kfree(name); + if (err) { + rsp->hdr.Status.CifsError = STATUS_INVALID_PARAMETER; + return err; + } + + return 0; +} + +/** + * smb1_is_sign_req() - handler for checking packet signing status + * @work: smb work containing notify command buffer + * + * Return: true if packed is signed, false otherwise + */ +bool smb1_is_sign_req(struct ksmbd_work *work, unsigned int command) +{ +#if 0 + struct smb_hdr *rcv_hdr1 = (struct smb_hdr *)work->request_buf; + + /* + * FIXME: signed tree connect failed by signing error + * with windows XP client. For now, Force to turn off + * signing feature in SMB1. + */ + if ((rcv_hdr1->Flags2 & SMBFLG2_SECURITY_SIGNATURE) && + command != SMB_COM_SESSION_SETUP_ANDX) + return true; + return false; +#else + return false; +#endif +} + +/** + * smb1_check_sign_req() - handler for req packet sign processing + * @work: smb work containing notify command buffer + * + * Return: 1 on success, 0 otherwise + */ +int smb1_check_sign_req(struct ksmbd_work *work) +{ + struct smb_hdr *rcv_hdr1 = (struct smb_hdr *)work->request_buf; + char signature_req[CIFS_SMB1_SIGNATURE_SIZE]; + char signature[20]; + struct kvec iov[1]; + + memcpy(signature_req, rcv_hdr1->Signature.SecuritySignature, + CIFS_SMB1_SIGNATURE_SIZE); + rcv_hdr1->Signature.Sequence.SequenceNumber = + cpu_to_le32(++work->sess->sequence_number); + rcv_hdr1->Signature.Sequence.Reserved = 0; + + iov[0].iov_base = rcv_hdr1->Protocol; + iov[0].iov_len = be32_to_cpu(rcv_hdr1->smb_buf_length); + + if (ksmbd_sign_smb1_pdu(work->sess, iov, 1, signature)) + return 0; + + if (memcmp(signature, signature_req, CIFS_SMB1_SIGNATURE_SIZE)) { + ksmbd_debug(SMB, "bad smb1 sign\n"); + return 0; + } + + return 1; +} + +/** + * smb1_set_sign_rsp() - handler for rsp packet sign processing + * @work: smb work containing notify command buffer + * + */ +void smb1_set_sign_rsp(struct ksmbd_work *work) +{ + struct smb_hdr *rsp_hdr = (struct smb_hdr *)work->response_buf; + char signature[20]; + struct kvec iov[2]; + int n_vec = 1; + + rsp_hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + rsp_hdr->Signature.Sequence.SequenceNumber = + cpu_to_le32(++work->sess->sequence_number); + rsp_hdr->Signature.Sequence.Reserved = 0; + + iov[0].iov_base = rsp_hdr->Protocol; + iov[0].iov_len = be32_to_cpu(rsp_hdr->smb_buf_length); + + if (work->aux_payload_sz) { + iov[0].iov_len -= work->aux_payload_sz; + + iov[1].iov_base = work->aux_payload_buf; + iov[1].iov_len = work->aux_payload_sz; + n_vec++; + } + + if (ksmbd_sign_smb1_pdu(work->sess, iov, n_vec, signature)) + memset(rsp_hdr->Signature.SecuritySignature, + 0, CIFS_SMB1_SIGNATURE_SIZE); + else + memcpy(rsp_hdr->Signature.SecuritySignature, + signature, CIFS_SMB1_SIGNATURE_SIZE); +} diff --git a/fs/ksmbd/smb1pdu.h b/fs/ksmbd/smb1pdu.h new file mode 100644 index 0000000000000..fb6c3959abcd1 --- /dev/null +++ b/fs/ksmbd/smb1pdu.h @@ -0,0 +1,1600 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __SMB1PDU_H +#define __SMB1PDU_H + +#define MAX_CIFS_HDR_SIZE 0x58 + +#define SMB1_CLIENT_GUID_SIZE (16) +#define SMB1_MAX_MPX_COUNT 10 +#define SMB1_MAX_VCS 1 +#define SMB1_MAX_RAW_SIZE 65536 +#define MAX_CIFS_LOOKUP_BUFFER_SIZE (16*1024) + +/* + * Size of the ntlm client response + */ +#define CIFS_AUTH_RESP_SIZE 24 +#define CIFS_SMB1_SIGNATURE_SIZE 8 +#define CIFS_SMB1_SESSKEY_SIZE 16 + +#define SMB1_SERVER_CAPS \ + (CAP_UNICODE | CAP_LARGE_FILES | CAP_EXTENDED_SECURITY |\ + CAP_NT_SMBS | CAP_STATUS32 | CAP_LOCK_AND_READ | \ + CAP_NT_FIND | CAP_UNIX | CAP_LARGE_READ_X | \ + CAP_LARGE_WRITE_X | CAP_LEVEL_II_OPLOCKS) + +#define SMB1_SERVER_SECU (SECMODE_USER | SECMODE_PW_ENCRYPT) + +/* Service Type of TreeConnect*/ +#define SERVICE_DISK_SHARE "A:" +#define SERVICE_IPC_SHARE "IPC" +#define SERVICE_PRINTER_SHARE "LPT1:" +#define SERVICE_COMM "COMM" + +#define NATIVE_FILE_SYSTEM "NTFS" + +#define SMB_NO_MORE_ANDX_COMMAND 0xFF +#define SMB1_PROTO_NUMBER cpu_to_le32(0x424d53ff) + +/* Transact2 subcommand codes */ +#define TRANS2_OPEN 0x00 +#define TRANS2_FIND_FIRST 0x01 +#define TRANS2_FIND_NEXT 0x02 +#define TRANS2_QUERY_FS_INFORMATION 0x03 +#define TRANS2_SET_FS_INFORMATION 0x04 +#define TRANS2_QUERY_PATH_INFORMATION 0x05 +#define TRANS2_SET_PATH_INFORMATION 0x06 +#define TRANS2_QUERY_FILE_INFORMATION 0x07 +#define TRANS2_SET_FILE_INFORMATION 0x08 +#define TRANS2_CREATE_DIRECTORY 0x0d +#define TRANS2_GET_DFS_REFERRAL 0x10 +#define TRANS2_REPORT_DFS_INCOSISTENCY 0x11 + +/* SMB Transact (Named Pipe) subcommand codes */ +#define TRANS_SET_NMPIPE_STATE 0x0001 +#define TRANS_RAW_READ_NMPIPE 0x0011 +#define TRANS_QUERY_NMPIPE_STATE 0x0021 +#define TRANS_QUERY_NMPIPE_INFO 0x0022 +#define TRANS_PEEK_NMPIPE 0x0023 +#define TRANS_TRANSACT_NMPIPE 0x0026 +#define TRANS_RAW_WRITE_NMPIPE 0x0031 +#define TRANS_READ_NMPIPE 0x0036 +#define TRANS_WRITE_NMPIPE 0x0037 +#define TRANS_WAIT_NMPIPE 0x0053 +#define TRANS_CALL_NMPIPE 0x0054 + +/* NT Transact subcommand codes */ +#define NT_TRANSACT_CREATE 0x01 +#define NT_TRANSACT_IOCTL 0x02 +#define NT_TRANSACT_SET_SECURITY_DESC 0x03 +#define NT_TRANSACT_NOTIFY_CHANGE 0x04 +#define NT_TRANSACT_RENAME 0x05 +#define NT_TRANSACT_QUERY_SECURITY_DESC 0x06 +#define NT_TRANSACT_GET_USER_QUOTA 0x07 +#define NT_TRANSACT_SET_USER_QUOTA 0x08 + +/* + * SMB flag definitions + */ +#define SMBFLG_EXTD_LOCK 0x01 /* server supports lock-read write-unlock smb */ +#define SMBFLG_RCV_POSTED 0x02 /* obsolete */ +#define SMBFLG_RSVD 0x04 +#define SMBFLG_CASELESS 0x08 /* + * all pathnames treated as caseless (off + * implies case sensitive file handling + * request) + */ +#define SMBFLG_CANONICAL_PATH_FORMAT 0x10 /* obsolete */ +#define SMBFLG_OLD_OPLOCK 0x20 /* obsolete */ +#define SMBFLG_OLD_OPLOCK_NOTIFY 0x40 /* obsolete */ +#define SMBFLG_RESPONSE 0x80 /* this PDU is a response from server */ + +/* + * SMB flag2 definitions + */ +#define SMBFLG2_KNOWS_LONG_NAMES cpu_to_le16(1) /* + * can send long (non-8.3) + * path names in response + */ +#define SMBFLG2_KNOWS_EAS cpu_to_le16(2) +#define SMBFLG2_SECURITY_SIGNATURE cpu_to_le16(4) +#define SMBFLG2_COMPRESSED (8) +#define SMBFLG2_SECURITY_SIGNATURE_REQUIRED (0x10) +#define SMBFLG2_IS_LONG_NAME cpu_to_le16(0x40) +#define SMBFLG2_REPARSE_PATH (0x400) +#define SMBFLG2_EXT_SEC cpu_to_le16(0x800) +#define SMBFLG2_DFS cpu_to_le16(0x1000) +#define SMBFLG2_PAGING_IO cpu_to_le16(0x2000) +#define SMBFLG2_ERR_STATUS cpu_to_le16(0x4000) +#define SMBFLG2_UNICODE cpu_to_le16(0x8000) + +#define SMB_COM_CREATE_DIRECTORY 0x00 /* trivial response */ +#define SMB_COM_DELETE_DIRECTORY 0x01 /* trivial response */ +#define SMB_COM_CLOSE 0x04 /* triv req/rsp, timestamp ignored */ +#define SMB_COM_FLUSH 0x05 /* triv req/rsp */ +#define SMB_COM_DELETE 0x06 /* trivial response */ +#define SMB_COM_RENAME 0x07 /* trivial response */ +#define SMB_COM_QUERY_INFORMATION 0x08 /* aka getattr */ +#define SMB_COM_SETATTR 0x09 /* trivial response */ +#define SMB_COM_WRITE 0x0b +#define SMB_COM_CHECK_DIRECTORY 0x10 /* trivial response */ +#define SMB_COM_PROCESS_EXIT 0x11 /* trivial response */ +#define SMB_COM_LOCKING_ANDX 0x24 /* trivial response */ +#define SMB_COM_TRANSACTION 0x25 +#define SMB_COM_COPY 0x29 /* trivial rsp, fail filename ignrd*/ +#define SMB_COM_ECHO 0x2B /* echo request */ +#define SMB_COM_OPEN_ANDX 0x2D /* Legacy open for old servers */ +#define SMB_COM_READ_ANDX 0x2E +#define SMB_COM_WRITE_ANDX 0x2F +#define SMB_COM_TRANSACTION2 0x32 +#define SMB_COM_TRANSACTION2_SECONDARY 0x33 +#define SMB_COM_FIND_CLOSE2 0x34 /* trivial response */ +#define SMB_COM_TREE_DISCONNECT 0x71 /* trivial response */ +#define SMB_COM_NEGOTIATE 0x72 +#define SMB_COM_SESSION_SETUP_ANDX 0x73 +#define SMB_COM_LOGOFF_ANDX 0x74 /* trivial response */ +#define SMB_COM_TREE_CONNECT_ANDX 0x75 +#define SMB_COM_NT_TRANSACT 0xA0 +#define SMB_COM_NT_TRANSACT_SECONDARY 0xA1 +#define SMB_COM_NT_CREATE_ANDX 0xA2 +#define SMB_COM_NT_CANCEL 0xA4 /* no response */ +#define SMB_COM_NT_RENAME 0xA5 /* trivial response */ + +/* Negotiate response Capabilities */ +#define CAP_RAW_MODE 0x00000001 +#define CAP_MPX_MODE 0x00000002 +#define CAP_UNICODE 0x00000004 +#define CAP_LARGE_FILES 0x00000008 +#define CAP_NT_SMBS 0x00000010 /* implies CAP_NT_FIND */ +#define CAP_RPC_REMOTE_APIS 0x00000020 +#define CAP_STATUS32 0x00000040 +#define CAP_LEVEL_II_OPLOCKS 0x00000080 +#define CAP_LOCK_AND_READ 0x00000100 +#define CAP_NT_FIND 0x00000200 +#define CAP_DFS 0x00001000 +#define CAP_INFOLEVEL_PASSTHRU 0x00002000 +#define CAP_LARGE_READ_X 0x00004000 +#define CAP_LARGE_WRITE_X 0x00008000 +#define CAP_LWIO 0x00010000 /* support fctl_srv_req_resume_key */ +#define CAP_UNIX 0x00800000 +#define CAP_COMPRESSED_DATA 0x02000000 +#define CAP_DYNAMIC_REAUTH 0x20000000 +#define CAP_PERSISTENT_HANDLES 0x40000000 +#define CAP_EXTENDED_SECURITY 0x80000000 + +/* RFC 1002 session packet types */ +#define RFC1002_SESSION_MESSAGE 0x00 +#define RFC1002_SESSION_REQUEST 0x81 +#define RFC1002_POSITIVE_SESSION_RESPONSE 0x82 +#define RFC1002_NEGATIVE_SESSION_RESPONSE 0x83 +#define RFC1002_RETARGET_SESSION_RESPONSE 0x84 +#define RFC1002_SESSION_KEEP_ALIVE 0x85 + +/* Action bits */ +#define GUEST_LOGIN 1 + +struct smb_com_read_req { + struct smb_hdr hdr; /* wct = 12 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __le32 OffsetLow; + __le16 MaxCount; + __le16 MinCount; /* obsolete */ + __le32 MaxCountHigh; + __le16 Remaining; + __le32 OffsetHigh; + __le16 ByteCount; +} __packed; + +struct smb_com_read_rsp { + struct smb_hdr hdr; /* wct = 12 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 Remaining; + __le16 DataCompactionMode; + __le16 Reserved; + __le16 DataLength; + __le16 DataOffset; + __le16 DataLengthHigh; + __u64 Reserved2; + __le16 ByteCount; + /* read response data immediately follows */ +} __packed; + +struct smb_com_write_req { + struct smb_hdr hdr; /* wct = 14 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __le32 OffsetLow; + __u32 Reserved; + __le16 WriteMode; + __le16 Remaining; + __le16 DataLengthHigh; + __le16 DataLengthLow; + __le16 DataOffset; + __le32 OffsetHigh; + __le16 ByteCount; + __u8 Pad; /* + * BB check for whether padded to DWORD + * boundary and optimum performance here + */ + char Data[0]; +} __packed; + +struct smb_com_write_req_32bit { + struct smb_hdr hdr; /* wct = 5 */ + __u16 Fid; + __le16 Length; + __le32 Offset; + __u16 Estimate; + __le16 ByteCount; /* must be greater than 2 */ + __u8 BufferFormat; + __u16 DataLength; + char Data[0]; +} __packed; + +struct smb_com_write_rsp_32bit { + struct smb_hdr hdr; /* wct = 1 */ + __le16 Written; + __le16 ByteCount; /* must be 0 */ +} __packed; + +struct smb_com_write_rsp { + struct smb_hdr hdr; /* wct = 6 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 Count; + __le16 Remaining; + __le16 CountHigh; + __u16 Reserved; + __le16 ByteCount; +} __packed; + +struct smb_com_rename_req { + struct smb_hdr hdr; /* wct = 1 */ + __le16 SearchAttributes; /* target file attributes */ + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII or Unicode */ + unsigned char OldFileName[1]; + /* followed by __u8 BufferFormat2 */ + /* followed by NewFileName */ +} __packed; + +struct smb_com_rename_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; /* bct = 0 */ +} __packed; + +/* SecurityMode bits */ +#define SECMODE_USER 0x01 /* off indicates share level security */ +#define SECMODE_PW_ENCRYPT 0x02 +#define SECMODE_SIGN_ENABLED 0x04 /* SMB security signatures enabled */ +#define SECMODE_SIGN_REQUIRED 0x08 /* SMB security signatures required */ + +struct smb_com_session_setup_req { /* request format */ + struct smb_hdr hdr; /* wct = 12 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 MaxBufferSize; + __le16 MaxMpxCount; + __le16 VcNumber; + __u32 SessionKey; + __le16 SecurityBlobLength; + __u32 Reserved; + __le32 Capabilities; /* see below */ + __le16 ByteCount; + unsigned char SecurityBlob[1]; /* followed by */ + /* STRING NativeOS */ + /* STRING NativeLanMan */ +} __packed; /* NTLM request format (with extended security) */ + +struct smb_com_session_setup_req_no_secext { /* request format */ + struct smb_hdr hdr; /* we will handle this :: wct = 13 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 MaxBufferSize; + __le16 MaxMpxCount; + __le16 VcNumber; + __u32 SessionKey; + __le16 CaseInsensitivePasswordLength; /* ASCII password len */ + __le16 CaseSensitivePasswordLength; /* Unicode password length*/ + __u32 Reserved; /* see below */ + __le32 Capabilities; + __le16 ByteCount; + unsigned char CaseInsensitivePassword[0]; /* followed by: */ + /* unsigned char * CaseSensitivePassword; */ + /* STRING AccountName */ + /* STRING PrimaryDomain */ + /* STRING NativeOS */ + /* STRING NativeLanMan */ +} __packed; /* NTLM request format (without extended security */ + +struct smb_com_session_setup_resp { /* default (NTLM) response format */ + struct smb_hdr hdr; /* wct = 4 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 Action; /* see below */ + __le16 SecurityBlobLength; + __le16 ByteCount; + unsigned char SecurityBlob[1]; /* followed by */ + /* unsigned char * NativeOS; */ + /* unsigned char * NativeLanMan; */ + /* unsigned char * PrimaryDomain; */ +} __packed; /* NTLM response (with or without extended sec) */ + +struct smb_com_session_setup_old_resp { /* default (NTLM) response format */ + struct smb_hdr hdr; /* wct = 3 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 Action; /* see below */ + __le16 ByteCount; + unsigned char NativeOS[1]; /* followed by */ + /* unsigned char * NativeLanMan; */ + /* unsigned char * PrimaryDomain; */ +} __packed; /* pre-NTLM (LANMAN2.1) response */ + +union smb_com_session_setup_andx { + struct smb_com_session_setup_req req; + struct smb_com_session_setup_req_no_secext req_no_secext; + struct smb_com_session_setup_resp resp; + struct smb_com_session_setup_old_resp old_resp; +} __packed; + +struct smb_com_tconx_req { + __u8 WordCount; /* wct = 4, it could be ANDX */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 Flags; /* see below */ + __le16 PasswordLength; + __le16 ByteCount; + unsigned char Password[1]; /* followed by */ + /* STRING Path *//* \\server\share name */ + /* STRING Service */ +} __packed; + +struct smb_com_tconx_rsp { + __u8 WordCount; /* wct = 3 , not extended response */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 OptionalSupport; /* see below */ + __le16 ByteCount; + unsigned char Service[1]; /* always ASCII, not Unicode */ + /* STRING NativeFileSystem */ +} __packed; + +struct smb_com_tconx_rsp_ext { + __u8 WordCount; /* wct = 7, extended response */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 OptionalSupport; /* see below */ + __le32 MaximalShareAccessRights; + __le32 GuestMaximalShareAccessRights; + __le16 ByteCount; + unsigned char Service[1]; /* always ASCII, not Unicode */ + /* STRING NativeFileSystem */ +} __packed; + +struct andx_block { + __u8 WordCount; + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; +} __packed; + +struct locking_andx_range64 { + __le16 Pid; + __le16 Pad; + __le32 OffsetHigh; + __le32 OffsetLow; + __le32 LengthHigh; + __le32 LengthLow; +} __packed; + +struct locking_andx_range32 { + __le16 Pid; + __le32 Offset; + __le32 Length; +} __packed; + +#define LOCKING_ANDX_SHARED_LOCK 0x01 +#define LOCKING_ANDX_OPLOCK_RELEASE 0x02 +#define LOCKING_ANDX_CHANGE_LOCKTYPE 0x04 +#define LOCKING_ANDX_CANCEL_LOCK 0x08 +#define LOCKING_ANDX_LARGE_FILES 0x10 /* always on for us */ + +struct smb_com_lock_req { + struct smb_hdr hdr; /* wct = 8 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __u8 LockType; + __u8 OplockLevel; + __le32 Timeout; + __le16 NumberOfUnlocks; + __le16 NumberOfLocks; + __le16 ByteCount; + char *Locks[1]; +} __packed; + +struct smb_com_lock_rsp { + struct smb_hdr hdr; /* wct = 2 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 ByteCount; +} __packed; + +/* tree connect Flags */ +#define DISCONNECT_TID 0x0001 +#define TCON_EXTENDED_SIGNATURES 0x0004 +#define TCON_EXTENDED_SECINFO 0x0008 + +/* OptionalSupport bits */ +#define SMB_SUPPORT_SEARCH_BITS 0x0001 /* + * "must have" directory search bits + * (exclusive searches supported) + */ +#define SMB_SHARE_IS_IN_DFS 0x0002 +#define SMB_CSC_MASK 0x000C +/* CSC flags defined as follows */ +#define SMB_CSC_CACHE_MANUAL_REINT 0x0000 +#define SMB_CSC_CACHE_AUTO_REINT 0x0004 +#define SMB_CSC_CACHE_VDO 0x0008 +#define SMB_CSC_NO_CACHING 0x000C +#define SMB_UNIQUE_FILE_NAME 0x0010 +#define SMB_EXTENDED_SIGNATURES 0x0020 + +/* OpenFlags */ +#define REQ_MORE_INFO 0x00000001 /* legacy (OPEN_AND_X) only */ +#define REQ_OPLOCK 0x00000002 +#define REQ_BATCHOPLOCK 0x00000004 +#define REQ_OPENDIRONLY 0x00000008 +#define REQ_EXTENDED_INFO 0x00000010 + +/* File type */ +#define DISK_TYPE 0x0000 +#define BYTE_PIPE_TYPE 0x0001 +#define MESSAGE_PIPE_TYPE 0x0002 +#define PRINTER_TYPE 0x0003 +#define COMM_DEV_TYPE 0x0004 +#define UNKNOWN_TYPE 0xFFFF + +/* Device Type or File Status Flags */ +#define NO_EAS 0x0001 +#define NO_SUBSTREAMS 0x0002 +#define NO_REPARSETAG 0x0004 +/* following flags can apply if pipe */ +#define ICOUNT_MASK 0x00FF +#define PIPE_READ_MODE 0x0100 +#define NAMED_PIPE_TYPE 0x0400 +#define PIPE_END_POINT 0x4000 +#define BLOCKING_NAMED_PIPE 0x8000 + +/* ShareAccess flags */ +#define FILE_NO_SHARE 0x00000000 +#define FILE_SHARE_READ 0x00000001 +#define FILE_SHARE_WRITE 0x00000002 +#define FILE_SHARE_DELETE 0x00000004 +#define FILE_SHARE_ALL 0x00000007 + +/* CreateDisposition flags, similar to CreateAction as well */ +#define FILE_SUPERSEDE 0x00000000 +#define FILE_OPEN 0x00000001 +#define FILE_CREATE 0x00000002 +#define FILE_OPEN_IF 0x00000003 +#define FILE_OVERWRITE 0x00000004 +#define FILE_OVERWRITE_IF 0x00000005 + +/* ImpersonationLevel flags */ +#define SECURITY_ANONYMOUS 0 +#define SECURITY_IDENTIFICATION 1 +#define SECURITY_IMPERSONATION 2 +#define SECURITY_DELEGATION 3 + +/* SecurityFlags */ +#define SECURITY_CONTEXT_TRACKING 0x01 +#define SECURITY_EFFECTIVE_ONLY 0x02 + +struct smb_com_open_req { /* also handles create */ + struct smb_hdr hdr; /* wct = 24 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u8 Reserved; /* Must Be Zero */ + __le16 NameLength; + __le32 OpenFlags; + __u32 RootDirectoryFid; + __le32 DesiredAccess; + __le64 AllocationSize; + __le32 FileAttributes; + __le32 ShareAccess; + __le32 CreateDisposition; + __le32 CreateOptions; + __le32 ImpersonationLevel; + __u8 SecurityFlags; + __le16 ByteCount; + char fileName[1]; +} __packed; + +/* open response for CreateAction shifted left */ +#define CIFS_CREATE_ACTION 0x20000 /* file created */ + +/* Basic file attributes */ +#define SMB_FILE_ATTRIBUTE_NORMAL 0x0000 +#define SMB_FILE_ATTRIBUTE_READONLY 0x0001 +#define SMB_FILE_ATTRIBUTE_HIDDEN 0x0002 +#define SMB_FILE_ATTRIBUTE_SYSTEM 0x0004 +#define SMB_FILE_ATTRIBUTE_VOLUME 0x0008 +#define SMB_FILE_ATTRIBUTE_DIRECTORY 0x0010 +#define SMB_FILE_ATTRIBUTE_ARCHIVE 0x0020 +#define SMB_SEARCH_ATTRIBUTE_READONLY 0x0100 +#define SMB_SEARCH_ATTRIBUTE_HIDDEN 0x0200 +#define SMB_SEARCH_ATTRIBUTE_SYSTEM 0x0400 +#define SMB_SEARCH_ATTRIBUTE_DIRECTORY 0x1000 +#define SMB_SEARCH_ATTRIBUTE_ARCHIVE 0x2000 + +struct smb_com_open_rsp { + struct smb_hdr hdr; /* wct = 34 BB */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u8 OplockLevel; + __u16 Fid; + __le32 CreateAction; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 FileAttributes; + __le64 AllocationSize; + __le64 EndOfFile; + __le16 FileType; + __le16 DeviceState; + __u8 DirectoryFlag; + __le16 ByteCount; /* bct = 0 */ +} __packed; + +struct smb_com_open_ext_rsp { + struct smb_hdr hdr; /* wct = 42 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u8 OplockLevel; + __u16 Fid; + __le32 CreateAction; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 FileAttributes; + __le64 AllocationSize; + __le64 EndOfFile; + __le16 FileType; + __le16 DeviceState; + __u8 DirectoryFlag; + __u8 VolId[16]; + __u64 fid; + __le32 MaxAccess; + __le32 GuestAccess; + __le16 ByteCount; /* bct = 0 */ +} __packed; + +struct smb_com_close_req { + struct smb_hdr hdr; /* wct = 3 */ + __u16 FileID; + __le32 LastWriteTime; /* should be zero or -1 */ + __le16 ByteCount; /* 0 */ +} __packed; + +struct smb_com_close_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; /* bct = 0 */ +} __packed; + +struct smb_com_echo_req { + struct smb_hdr hdr; + __le16 EchoCount; + __le16 ByteCount; + char Data[1]; +} __packed; + +struct smb_com_echo_rsp { + struct smb_hdr hdr; + __le16 SequenceNumber; + __le16 ByteCount; + char Data[1]; +} __packed; + +struct smb_com_flush_req { + struct smb_hdr hdr; /* wct = 1 */ + __u16 FileID; + __le16 ByteCount; /* 0 */ +} __packed; + +struct smb_com_flush_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; /* bct = 0 */ +} __packed; + +/* SMB_COM_TRANSACTION */ +struct smb_com_trans_req { + struct smb_hdr hdr; + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; + __u8 Pad; + __u8 Data[1]; +} __packed; + +struct smb_com_trans_pipe_req { + struct smb_hdr hdr; + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __u16 SubCommand; + __u16 fid; + __le16 ByteCount; + __u8 Pad; + __u8 Data[1]; +} __packed; + +struct smb_com_trans_rsp { + struct smb_hdr hdr; /* wct = 10+ */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __u16 Reserved; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 ParameterDisplacement; + __le16 DataCount; + __le16 DataOffset; + __le16 DataDisplacement; + __u8 SetupCount; + __u8 Reserved1; + __le16 ByteCount; + __u8 Pad; +} __packed; + +/* SMB_COM_TRANSACTION subcommands */ + +#define TRANSACT_DCERPCCMD 0x26 + +/***************************************************************************** + * TRANS2 command implementation functions + *****************************************************************************/ +#define NO_CHANGE_64 0xFFFFFFFFFFFFFFFFULL + +/* QFSInfo Levels */ +#define SMB_INFO_ALLOCATION 1 +#define SMB_INFO_VOLUME 2 +#define SMB_QUERY_FS_VOLUME_INFO 0x102 +#define SMB_QUERY_FS_SIZE_INFO 0x103 +#define SMB_QUERY_FS_DEVICE_INFO 0x104 +#define SMB_QUERY_FS_ATTRIBUTE_INFO 0x105 +#define SMB_QUERY_CIFS_UNIX_INFO 0x200 +#define SMB_QUERY_POSIX_FS_INFO 0x201 +#define SMB_QUERY_POSIX_WHO_AM_I 0x202 +#define SMB_REQUEST_TRANSPORT_ENCRYPTION 0x203 +#define SMB_QUERY_FS_PROXY 0x204 /* + * WAFS enabled. Returns structure + * FILE_SYSTEM__UNIX_INFO to tell + * whether new NTIOCTL available + * (0xACE) for WAN friendly SMB + * operations to be carried + */ +#define SMB_QUERY_LABEL_INFO 0x3ea +#define SMB_QUERY_FS_QUOTA_INFO 0x3ee +#define SMB_QUERY_FS_FULL_SIZE_INFO 0x3ef +#define SMB_QUERY_OBJECTID_INFO 0x3f0 + +struct trans2_resp { + /* struct smb_hdr hdr precedes. Note wct = 10 + setup count */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __u16 Reserved; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 ParameterDisplacement; + __le16 DataCount; + __le16 DataOffset; + __le16 DataDisplacement; + __u8 SetupCount; + __u8 Reserved1; + /* + * SetupWords[SetupCount]; + * __u16 ByteCount; + * __u16 Reserved2; + */ + /* data area follows */ +} __packed; + +struct smb_com_trans2_req { + struct smb_hdr hdr; + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; /* one setup word */ +} __packed; + +struct smb_com_trans2_qfsi_req { + struct smb_hdr hdr; /* wct = 14+ */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; /* one setup word */ + __le16 ByteCount; + __u8 Pad; + __le16 InformationLevel; +} __packed; + +struct smb_com_trans2_qfsi_req_params { + __le16 InformationLevel; +} __packed; + +#define CIFS_SEARCH_CLOSE_ALWAYS 0x0001 +#define CIFS_SEARCH_CLOSE_AT_END 0x0002 +#define CIFS_SEARCH_RETURN_RESUME 0x0004 +#define CIFS_SEARCH_CONTINUE_FROM_LAST 0x0008 +#define CIFS_SEARCH_BACKUP_SEARCH 0x0010 + +struct smb_com_trans2_ffirst_req_params { + __le16 SearchAttributes; + __le16 SearchCount; + __le16 SearchFlags; + __le16 InformationLevel; + __le32 SearchStorageType; + char FileName[1]; +} __packed; + +struct smb_com_trans2_ffirst_rsp_parms { + __u16 SearchHandle; + __le16 SearchCount; + __le16 EndofSearch; + __le16 EAErrorOffset; + __le16 LastNameOffset; +} __packed; + +struct smb_com_trans2_fnext_req_params { + __u16 SearchHandle; + __le16 SearchCount; + __le16 InformationLevel; + __u32 ResumeKey; + __le16 SearchFlags; + char ResumeFileName[1]; +} __packed; + +struct smb_com_trans2_fnext_rsp_params { + __le16 SearchCount; + __le16 EndofSearch; + __le16 EAErrorOffset; + __le16 LastNameOffset; +} __packed; + +struct smb_com_trans2_rsp { + struct smb_hdr hdr; /* wct = 10 + SetupCount */ + struct trans2_resp t2; + __le16 ByteCount; + __u8 Pad; /* may be three bytes? *//* followed by data area */ + __u8 Buffer[0]; +} __packed; + +struct file_internal_info { + __le64 UniqueId; /* inode number */ +} __packed; /* level 0x3ee */ + +/* DeviceType Flags */ +#define FILE_DEVICE_CD_ROM 0x00000002 +#define FILE_DEVICE_CD_ROM_FILE_SYSTEM 0x00000003 +#define FILE_DEVICE_DFS 0x00000006 +#define FILE_DEVICE_DISK 0x00000007 +#define FILE_DEVICE_DISK_FILE_SYSTEM 0x00000008 +#define FILE_DEVICE_FILE_SYSTEM 0x00000009 +#define FILE_DEVICE_NAMED_PIPE 0x00000011 +#define FILE_DEVICE_NETWORK 0x00000012 +#define FILE_DEVICE_NETWORK_FILE_SYSTEM 0x00000014 +#define FILE_DEVICE_NULL 0x00000015 +#define FILE_DEVICE_PARALLEL_PORT 0x00000016 +#define FILE_DEVICE_PRINTER 0x00000018 +#define FILE_DEVICE_SERIAL_PORT 0x0000001b +#define FILE_DEVICE_STREAMS 0x0000001e +#define FILE_DEVICE_TAPE 0x0000001f +#define FILE_DEVICE_TAPE_FILE_SYSTEM 0x00000020 +#define FILE_DEVICE_VIRTUAL_DISK 0x00000024 +#define FILE_DEVICE_NETWORK_REDIRECTOR 0x00000028 + +/* Filesystem Attributes. */ +#define FILE_CASE_SENSITIVE_SEARCH 0x00000001 +#define FILE_CASE_PRESERVED_NAMES 0x00000002 +#define FILE_UNICODE_ON_DISK 0x00000004 +/* According to cifs9f, this is 4, not 8 */ +/* Acconding to testing, this actually sets the security attribute! */ +#define FILE_PERSISTENT_ACLS 0x00000008 +#define FILE_FILE_COMPRESSION 0x00000010 +#define FILE_VOLUME_QUOTAS 0x00000020 +#define FILE_SUPPORTS_SPARSE_FILES 0x00000040 +#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080 +#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100 +#define FS_LFN_APIS 0x00004000 +#define FILE_VOLUME_IS_COMPRESSED 0x00008000 +#define FILE_SUPPORTS_OBJECT_IDS 0x00010000 +#define FILE_SUPPORTS_ENCRYPTION 0x00020000 +#define FILE_NAMED_STREAMS 0x00040000 +#define FILE_READ_ONLY_VOLUME 0x00080000 + +/* PathInfo/FileInfo infolevels */ +#define SMB_INFO_STANDARD 1 +#define SMB_SET_FILE_EA 2 +#define SMB_QUERY_FILE_EA_SIZE 2 +#define SMB_INFO_QUERY_EAS_FROM_LIST 3 +#define SMB_INFO_QUERY_ALL_EAS 4 +#define SMB_INFO_IS_NAME_VALID 6 +#define SMB_QUERY_FILE_BASIC_INFO 0x101 +#define SMB_QUERY_FILE_STANDARD_INFO 0x102 +#define SMB_QUERY_FILE_EA_INFO 0x103 +#define SMB_QUERY_FILE_NAME_INFO 0x104 +#define SMB_QUERY_FILE_ALLOCATION_INFO 0x105 +#define SMB_QUERY_FILE_END_OF_FILEINFO 0x106 +#define SMB_QUERY_FILE_ALL_INFO 0x107 +#define SMB_QUERY_ALT_NAME_INFO 0x108 +#define SMB_QUERY_FILE_STREAM_INFO 0x109 +#define SMB_QUERY_FILE_COMPRESSION_INFO 0x10B +#define SMB_QUERY_FILE_UNIX_BASIC 0x200 +#define SMB_QUERY_FILE_UNIX_LINK 0x201 +#define SMB_QUERY_POSIX_ACL 0x204 +#define SMB_QUERY_XATTR 0x205 /* e.g. system EA name space */ +#define SMB_QUERY_ATTR_FLAGS 0x206 /* append,immutable etc. */ +#define SMB_QUERY_POSIX_PERMISSION 0x207 +#define SMB_QUERY_POSIX_LOCK 0x208 +/* #define SMB_POSIX_OPEN 0x209 */ +/* #define SMB_POSIX_UNLINK 0x20a */ +#define SMB_QUERY_FILE__UNIX_INFO2 0x20b +#define SMB_QUERY_FILE_INTERNAL_INFO 0x3ee +#define SMB_QUERY_FILE_ACCESS_INFO 0x3f0 +#define SMB_QUERY_FILE_NAME_INFO2 0x3f1 /* 0x30 bytes */ +#define SMB_QUERY_FILE_POSITION_INFO 0x3f6 +#define SMB_QUERY_FILE_MODE_INFO 0x3f8 +#define SMB_QUERY_FILE_ALGN_INFO 0x3f9 + + +#define SMB_SET_FILE_BASIC_INFO 0x101 +#define SMB_SET_FILE_DISPOSITION_INFO 0x102 +#define SMB_SET_FILE_ALLOCATION_INFO 0x103 +#define SMB_SET_FILE_END_OF_FILE_INFO 0x104 +#define SMB_SET_FILE_UNIX_BASIC 0x200 +#define SMB_SET_FILE_UNIX_LINK 0x201 +#define SMB_SET_FILE_UNIX_HLINK 0x203 +#define SMB_SET_POSIX_ACL 0x204 +#define SMB_SET_XATTR 0x205 +#define SMB_SET_ATTR_FLAGS 0x206 /* append, immutable etc. */ +#define SMB_SET_POSIX_LOCK 0x208 +#define SMB_POSIX_OPEN 0x209 +#define SMB_POSIX_UNLINK 0x20a +#define SMB_SET_FILE_UNIX_INFO2 0x20b +#define SMB_SET_FILE_BASIC_INFO2 0x3ec +#define SMB_SET_FILE_RENAME_INFORMATION 0x3f2 /* BB check if qpathinfo too */ +#define SMB_SET_FILE_DISPOSITION_INFORMATION 0x3f5 /* alias for 0x102 */ +#define SMB_FILE_ALL_INFO2 0x3fa +#define SMB_SET_FILE_ALLOCATION_INFO2 0x3fb +#define SMB_SET_FILE_END_OF_FILE_INFO2 0x3fc +#define SMB_FILE_MOVE_CLUSTER_INFO 0x407 +#define SMB_FILE_QUOTA_INFO 0x408 +#define SMB_FILE_REPARSEPOINT_INFO 0x409 +#define SMB_FILE_MAXIMUM_INFO 0x40d + +/* Find File infolevels */ +#define SMB_FIND_FILE_INFO_STANDARD 0x001 +#define SMB_FIND_FILE_QUERY_EA_SIZE 0x002 +#define SMB_FIND_FILE_QUERY_EAS_FROM_LIST 0x003 +#define SMB_FIND_FILE_DIRECTORY_INFO 0x101 +#define SMB_FIND_FILE_FULL_DIRECTORY_INFO 0x102 +#define SMB_FIND_FILE_NAMES_INFO 0x103 +#define SMB_FIND_FILE_BOTH_DIRECTORY_INFO 0x104 +#define SMB_FIND_FILE_ID_FULL_DIR_INFO 0x105 +#define SMB_FIND_FILE_ID_BOTH_DIR_INFO 0x106 +#define SMB_FIND_FILE_UNIX 0x202 + +struct smb_com_trans2_qpi_req { + struct smb_hdr hdr; /* wct = 14+ */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; /* one setup word */ + __le16 ByteCount; + __u8 Pad; + __le16 InformationLevel; + __u32 Reserved4; + char FileName[1]; +} __packed; + +struct trans2_qpi_req_params { + __le16 InformationLevel; + __u32 Reserved4; + char FileName[1]; +} __packed; + +/******************************************************************************/ +/* QueryFileInfo/QueryPathinfo (also for SetPath/SetFile) data buffer formats */ +/******************************************************************************/ +struct file_basic_info { + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 Attributes; + __u32 Pad; +} __packed; /* size info, level 0x101 */ + +struct file_standard_info { + __le64 AllocationSize; + __le64 EndOfFile; + __le32 NumberOfLinks; + __u8 DeletePending; + __u8 Directory; + __le16 Reserved; +} __packed; + +struct file_ea_info { + __le32 EaSize; +} __packed; + +struct alt_name_info { + __le32 FileNameLength; + char FileName[1]; +} __packed; + +struct file_name_info { + __le32 FileNameLength; + char FileName[1]; +} __packed; + +/* data block encoding of response to level 263 QPathInfo */ +struct file_all_info { + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 Attributes; + __u32 Pad1; + __le64 AllocationSize; + __le64 EndOfFile; /* size ie offset to first free byte in file */ + __le32 NumberOfLinks; /* hard links */ + __u8 DeletePending; + __u8 Directory; + __u16 Pad2; + __le32 EASize; + __le32 FileNameLength; + char FileName[1]; +} __packed; /* level 0x107 QPathInfo */ + +/* set path info/open file */ +/* defines for enumerating possible values of the Unix type field below */ +#define UNIX_FILE 0 +#define UNIX_DIR 1 +#define UNIX_SYMLINK 2 +#define UNIX_CHARDEV 3 +#define UNIX_BLOCKDEV 4 +#define UNIX_FIFO 5 +#define UNIX_SOCKET 6 +#define UNIX_UNKNOWN 0xFFFFFFFF + +struct file_unix_basic_info { + __le64 EndOfFile; + __le64 NumOfBytes; + __le64 LastStatusChange; /*SNIA specs DCE time for the 3 time fields */ + __le64 LastAccessTime; + __le64 LastModificationTime; + __le64 Uid; + __le64 Gid; + __le32 Type; + __le64 DevMajor; + __le64 DevMinor; + __le64 UniqueId; + __le64 Permissions; + __le64 Nlinks; +} __packed; /* level 0x200 QPathInfo */ + +struct smb_com_trans2_spi_req { + struct smb_hdr hdr; /* wct = 15 */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; /* one setup word */ + __le16 ByteCount; + __u8 Pad; + __u16 Pad1; + __le16 InformationLevel; + __u32 Reserved4; + char FileName[1]; +} __packed; + +struct smb_com_trans2_spi_rsp { + struct smb_hdr hdr; /* wct = 10 + SetupCount */ + struct trans2_resp t2; + __le16 ByteCount; + __u16 Reserved2; /* parameter word is present for infolevels > 100 */ +} __packed; + +/* POSIX Open Flags */ +#define SMB_O_RDONLY 0x1 +#define SMB_O_WRONLY 0x2 +#define SMB_O_RDWR 0x4 +#define SMB_O_CREAT 0x10 +#define SMB_O_EXCL 0x20 +#define SMB_O_TRUNC 0x40 +#define SMB_O_APPEND 0x80 +#define SMB_O_SYNC 0x100 +#define SMB_O_DIRECTORY 0x200 +#define SMB_O_NOFOLLOW 0x400 +#define SMB_O_DIRECT 0x800 +#define SMB_ACCMODE 0x7 + +/* info level response for SMB_POSIX_PATH_OPEN */ +#define SMB_NO_INFO_LEVEL_RESPONSE 0xFFFF + +struct open_psx_req { + __le32 OpenFlags; /* same as NT CreateX */ + __le32 PosixOpenFlags; + __le64 Permissions; + __le16 Level; /* reply level requested (see QPathInfo levels) */ +} __packed; /* level 0x209 SetPathInfo data */ + +struct open_psx_rsp { + __le16 OplockFlags; + __u16 Fid; + __le32 CreateAction; + __le16 ReturnedLevel; + __le16 Pad; + /* struct following varies based on requested level */ +} __packed; /* level 0x209 SetPathInfo data */ + +struct unlink_psx_rsp { + __le16 EAErrorOffset; +} __packed; /* level 0x209 SetPathInfo data*/ + +/* Version numbers for CIFS UNIX major and minor. */ +#define CIFS_UNIX_MAJOR_VERSION 1 +#define CIFS_UNIX_MINOR_VERSION 0 + +struct filesystem_unix_info { + __le16 MajorVersionNumber; + __le16 MinorVersionNumber; + __le64 Capability; +} __packed; /* Unix extension level 0x200*/ + +/* Linux/Unix extensions capability flags */ +#define CIFS_UNIX_FCNTL_CAP 0x00000001 /* support for fcntl locks */ +#define CIFS_UNIX_POSIX_ACL_CAP 0x00000002 /* support getfacl/setfacl */ +#define CIFS_UNIX_XATTR_CAP 0x00000004 /* support new namespace */ +#define CIFS_UNIX_EXTATTR_CAP 0x00000008 /* support chattr/chflag */ +#define CIFS_UNIX_POSIX_PATHNAMES_CAP 0x00000010 /* Allow POSIX path chars */ +#define CIFS_UNIX_POSIX_PATH_OPS_CAP 0x00000020 /* + * Allow new POSIX path based + * calls including posix open + * and posix unlink + */ +#define CIFS_UNIX_LARGE_READ_CAP 0x00000040 /* + * support reads >128K (up + * to 0xFFFF00 + */ +#define CIFS_UNIX_LARGE_WRITE_CAP 0x00000080 +#define CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP 0x00000100 /* can do SPNEGO crypt */ +#define CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP 0x00000200 /* must do */ +#define CIFS_UNIX_PROXY_CAP 0x00000400 /* + * Proxy cap: 0xACE ioctl and + * QFS PROXY call + */ +#ifdef CONFIG_CIFS_POSIX +/* presumably don't need the 0x20 POSIX_PATH_OPS_CAP since we never send + * LockingX instead of posix locking call on unix sess (and we do not expect + * LockingX to use different (ie Windows) semantics than posix locking on + * the same session (if WINE needs to do this later, we can add this cap + * back in later + */ + +/* #define CIFS_UNIX_CAP_MASK 0x000000fb */ +#define CIFS_UNIX_CAP_MASK 0x000003db +#else +#define CIFS_UNIX_CAP_MASK 0x00000013 +#endif /* CONFIG_CIFS_POSIX */ + + +#define CIFS_POSIX_EXTENSIONS 0x00000010 /* support for new QFSInfo */ + +/* Our server caps */ + +#define SMB_UNIX_CAPS (CIFS_UNIX_FCNTL_CAP | CIFS_UNIX_POSIX_ACL_CAP | \ + CIFS_UNIX_XATTR_CAP | CIFS_UNIX_POSIX_PATHNAMES_CAP| \ + CIFS_UNIX_POSIX_PATH_OPS_CAP | CIFS_UNIX_LARGE_READ_CAP | \ + CIFS_UNIX_LARGE_WRITE_CAP) + +#define SMB_SET_CIFS_UNIX_INFO 0x200 +/* Level 0x200 request structure follows */ +struct smb_com_trans2_setfsi_req { + struct smb_hdr hdr; /* wct = 15 */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; /* 4 */ + __le16 ParameterOffset; + __le16 DataCount; /* 12 */ + __le16 DataOffset; + __u8 SetupCount; /* one */ + __u8 Reserved3; + __le16 SubCommand; /* TRANS2_SET_FS_INFORMATION */ + __le16 ByteCount; + __u8 Pad; + __u16 FileNum; /* Parameters start. */ + __le16 InformationLevel;/* Parameters end. */ + __le16 ClientUnixMajor; /* Data start. */ + __le16 ClientUnixMinor; + __le64 ClientUnixCap; /* Data end */ +} __packed; + +/* response for setfsinfo levels 0x200 and 0x203 */ +struct smb_com_trans2_setfsi_rsp { + struct smb_hdr hdr; /* wct = 10 */ + struct trans2_resp t2; + __le16 ByteCount; +} __packed; + +struct smb_trans2_qfi_req_params { + __u16 Fid; + __le16 InformationLevel; +} __packed; + +/* FIND FIRST2 and FIND NEXT2 INFORMATION Level Codes*/ + +struct find_info_standard { + __le16 CreationDate; /* SMB Date see above */ + __le16 CreationTime; /* SMB Time */ + __le16 LastAccessDate; + __le16 LastAccessTime; + __le16 LastWriteDate; + __le16 LastWriteTime; + __le32 DataSize; /* File Size (EOF) */ + __le32 AllocationSize; + __le16 Attributes; /* verify not u32 */ + __le16 FileNameLength; + char FileName[1]; +} __packed; + +struct find_info_query_ea_size { + __le16 CreationDate; /* SMB Date see above */ + __le16 CreationTime; /* SMB Time */ + __le16 LastAccessDate; + __le16 LastAccessTime; + __le16 LastWriteDate; + __le16 LastWriteTime; + __le32 DataSize; /* File Size (EOF) */ + __le32 AllocationSize; + __le16 Attributes; /* verify not u32 */ + __le32 EASize; + __u8 FileNameLength; + char FileName[1]; +} __packed; + +struct file_unix_info { + __le32 NextEntryOffset; + __u32 ResumeKey; /* as with FileIndex - no need to convert */ + struct file_unix_basic_info basic; + char FileName[1]; +} __packed; /* level 0x202 */ + +struct smb_com_trans2_sfi_req { + struct smb_hdr hdr; /* wct = 15 */ + __le16 TotalParameterCount; + __le16 TotalDataCount; + __le16 MaxParameterCount; + __le16 MaxDataCount; + __u8 MaxSetupCount; + __u8 Reserved; + __le16 Flags; + __le32 Timeout; + __u16 Reserved2; + __le16 ParameterCount; + __le16 ParameterOffset; + __le16 DataCount; + __le16 DataOffset; + __u8 SetupCount; + __u8 Reserved3; + __le16 SubCommand; /* one setup word */ + __le16 ByteCount; + __u8 Pad; + __u16 Pad1; + __u16 Fid; + __le16 InformationLevel; + __u16 Reserved4; +} __packed; + +struct smb_com_trans2_sfi_rsp { + struct smb_hdr hdr; /* wct = 10 + SetupCount */ + struct trans2_resp t2; + __le16 ByteCount; + __u16 Reserved2; /* + * parameter word reserved - + * present for infolevels > 100 + */ +} __packed; + +struct file_end_of_file_info { + __le64 FileSize; /* offset to end of file */ +} __packed; /* size info, level 0x104 for set, 0x106 for query */ + +struct smb_com_create_directory_req { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII */ + unsigned char DirName[1]; +} __packed; + +struct smb_com_create_directory_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; /* bct = 0 */ +} __packed; + +struct smb_com_check_directory_req { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII */ + unsigned char DirName[1]; +} __packed; + +struct smb_com_check_directory_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; /* bct = 0 */ +} __packed; + +struct smb_com_process_exit_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; /* bct = 0 */ +} __packed; + +struct smb_com_delete_directory_req { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII */ + unsigned char DirName[1]; +} __packed; + +struct smb_com_delete_directory_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; /* bct = 0 */ +} __packed; + +struct smb_com_delete_file_req { + struct smb_hdr hdr; /* wct = 1 */ + __le16 SearchAttributes; + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII */ + unsigned char fileName[1]; +} __packed; + +struct smb_com_delete_file_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; /* bct = 0 */ +} __packed; + +#define CREATE_HARD_LINK 0x103 + +struct smb_com_nt_rename_req { /* A5 - also used for create hardlink */ + struct smb_hdr hdr; /* wct = 4 */ + __le16 SearchAttributes; /* target file attributes */ + __le16 Flags; /* spec says Information Level */ + __le32 ClusterCount; + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII or Unicode */ + unsigned char OldFileName[1]; + /* followed by __u8 BufferFormat2 */ + /* followed by NewFileName */ +} __packed; + +struct smb_com_query_information_req { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; /* 1 + namelen + 1 */ + __u8 BufferFormat; /* 4 = ASCII */ + unsigned char FileName[1]; +} __packed; + +struct smb_com_query_information_rsp { + struct smb_hdr hdr; /* wct = 10 */ + __le16 attr; + __le32 last_write_time; + __le32 size; + __u16 reserved[5]; + __le16 ByteCount; /* bcc = 0 */ +} __packed; + +struct smb_com_findclose_req { + struct smb_hdr hdr; /* wct = 1 */ + __u16 FileID; + __le16 ByteCount; /* 0 */ +} __packed; + +#define SMBOPEN_DISPOSITION_NONE 0 +#define SMBOPEN_LOCK_GRANTED 0x8000 + +#define SMB_DA_ACCESS_READ 0 +#define SMB_DA_ACCESS_WRITE 0x0001 +#define SMB_DA_ACCESS_READ_WRITE 0x0002 + +/* + * Flags on SMB open + */ +#define SMBOPEN_WRITE_THROUGH 0x4000 +#define SMBOPEN_DENY_ALL 0x0010 +#define SMBOPEN_DENY_WRITE 0x0020 +#define SMBOPEN_DENY_READ 0x0030 +#define SMBOPEN_DENY_NONE 0x0040 +#define SMBOPEN_SHARING_MODE (SMBOPEN_DENY_ALL | \ + SMBOPEN_DENY_WRITE | \ + SMBOPEN_DENY_READ | \ + SMBOPEN_DENY_NONE) +#define SMBOPEN_READ 0x0000 +#define SMBOPEN_WRITE 0x0001 +#define SMBOPEN_READWRITE 0x0002 +#define SMBOPEN_EXECUTE 0x0003 + +#define SMBOPEN_OCREATE 0x0010 +#define SMBOPEN_OTRUNC 0x0002 +#define SMBOPEN_OAPPEND 0x0001 + +/* format of legacy open request */ +struct smb_com_openx_req { + struct smb_hdr hdr; /* wct = 15 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __le16 OpenFlags; + __le16 Mode; + __le16 Sattr; /* search attributes */ + __le16 FileAttributes; /* dos attrs */ + __le32 CreateTime; /* os2 format */ + __le16 OpenFunction; + __le32 EndOfFile; + __le32 Timeout; + __le32 Reserved; + __le16 ByteCount; /* file name follows */ + char fileName[1]; +} __packed; + +struct smb_com_openx_rsp { + struct smb_hdr hdr; /* wct = 15 */ + __u8 AndXCommand; + __u8 AndXReserved; + __le16 AndXOffset; + __u16 Fid; + __le16 FileAttributes; + __le32 LastWriteTime; /* os2 format */ + __le32 EndOfFile; + __le16 Access; + __le16 FileType; + __le16 IPCState; + __le16 Action; + __u32 FileId; + __u16 Reserved; + __le16 ByteCount; +} __packed; + +struct filesystem_alloc_info { + __le32 fsid; + __le32 SectorsPerAllocationUnit; + __le32 TotalAllocationUnits; + __le32 FreeAllocationUnits; + __le16 BytesPerSector; +} __packed; + +struct file_allocation_info { + __le64 AllocationSize; /* Note old Samba srvr rounds this up too much */ +} __packed; /* size used on disk: 0x103 for set, 0x105 for query */ + +struct file_info_standard { + __le16 CreationDate; /* SMB Date see above */ + __le16 CreationTime; /* SMB Time */ + __le16 LastAccessDate; + __le16 LastAccessTime; + __le16 LastWriteDate; + __le16 LastWriteTime; + __le32 DataSize; /* File Size (EOF) */ + __le32 AllocationSize; + __le16 Attributes; /* verify not u32 */ + __le32 EASize; +} __packed; /* level 1 SetPath/FileInfo */ + +#define CIFS_MF_SYMLINK_LINK_MAXLEN (1024) + +struct set_file_rename { + __le32 overwrite; /* 1 = overwrite dest */ + __u32 root_fid; /* zero */ + __le32 target_name_len; + char target_name[0]; /* Must be unicode */ +} __packed; + +struct fea { + unsigned char EA_flags; + __u8 name_len; + __le16 value_len; + char name[1]; + /* optionally followed by value */ +} __packed; + +struct fealist { + __le32 list_len; + __u8 list[1]; +} __packed; + +/* POSIX ACL set/query path info structures */ +#define CIFS_ACL_VERSION 1 +struct cifs_posix_ace { /* access control entry (ACE) */ + __u8 cifs_e_tag; + __u8 cifs_e_perm; + __le64 cifs_uid; /* or gid */ +} __packed; + +struct cifs_posix_acl { /* access conrol list (ACL) */ + __le16 version; + __le16 access_entry_count; /* access ACL - count of entries */ + __le16 default_entry_count; /* default ACL - count of entries */ + struct cifs_posix_ace ace_array[0]; + /* + * followed by + * struct cifs_posix_ace default_ace_arraay[] + */ +} __packed; /* level 0x204 */ + +struct smb_com_setattr_req { + struct smb_hdr hdr; /* wct = 8 */ + __le16 attr; + __le32 LastWriteTime; + __le16 reserved[5]; /* must be zero */ + __le16 ByteCount; + __u8 BufferFormat; /* 4 = ASCII */ + unsigned char fileName[1]; +} __packed; + +struct smb_com_setattr_rsp { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; /* bct = 0 */ +} __packed; + +extern int init_smb1_server(struct ksmbd_conn *conn); + +/* function prototypes */ +extern int init_smb_rsp_hdr(struct ksmbd_work *work); +extern u16 get_smb_cmd_val(struct ksmbd_work *work); +extern void set_smb_rsp_status(struct ksmbd_work *work, __le32 err); +extern int smb_allocate_rsp_buf(struct ksmbd_work *work); +extern bool smb1_is_sign_req(struct ksmbd_work *work, unsigned int command); +extern int smb1_check_sign_req(struct ksmbd_work *work); +extern void smb1_set_sign_rsp(struct ksmbd_work *work); +extern int smb_check_user_session(struct ksmbd_work *work); +extern int smb_get_ksmbd_tcon(struct ksmbd_work *work); +extern int ksmbd_smb1_check_message(struct ksmbd_work *work); + +/* smb1 command handlers */ +extern int smb_rename(struct ksmbd_work *work); +extern int smb_negotiate_request(struct ksmbd_work *work); +extern int smb_handle_negotiate(struct ksmbd_work *work); +extern int smb_session_setup_andx(struct ksmbd_work *work); +extern int smb_tree_connect_andx(struct ksmbd_work *work); +extern int smb_trans2(struct ksmbd_work *work); +extern int smb_nt_create_andx(struct ksmbd_work *work); +extern int smb_trans(struct ksmbd_work *work); +extern int smb_locking_andx(struct ksmbd_work *work); +extern int smb_close(struct ksmbd_work *work); +extern int smb_read_andx(struct ksmbd_work *work); +extern int smb_tree_disconnect(struct ksmbd_work *work); +extern int smb_session_disconnect(struct ksmbd_work *work); +extern int smb_write_andx(struct ksmbd_work *work); +extern int smb_echo(struct ksmbd_work *work); +extern int smb_flush(struct ksmbd_work *work); +extern int smb_mkdir(struct ksmbd_work *work); +extern int smb_rmdir(struct ksmbd_work *work); +extern int smb_unlink(struct ksmbd_work *work); +extern int smb_nt_cancel(struct ksmbd_work *work); +extern int smb_nt_rename(struct ksmbd_work *work); +extern int smb_query_info(struct ksmbd_work *work); +extern int smb_closedir(struct ksmbd_work *work); +extern int smb_open_andx(struct ksmbd_work *work); +extern int smb_write(struct ksmbd_work *work); +extern int smb_setattr(struct ksmbd_work *work); +extern int smb_checkdir(struct ksmbd_work *work); +extern int smb_process_exit(struct ksmbd_work *work); +#endif /* __SMB1PDU_H */ diff --git a/fs/ksmbd/smb2misc.c b/fs/ksmbd/smb2misc.c new file mode 100644 index 0000000000000..de500b2a54226 --- /dev/null +++ b/fs/ksmbd/smb2misc.c @@ -0,0 +1,450 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include "glob.h" +#include "nterr.h" +#include "smb2pdu.h" +#include "smb_common.h" +#include "smbstatus.h" +#include "mgmt/user_session.h" +#include "connection.h" + +static int check_smb2_hdr(struct smb2_hdr *hdr) +{ + /* + * Make sure that this really is an SMB, that it is a response. + */ + if (hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) + return 1; + return 0; +} + +/* + * The following table defines the expected "StructureSize" of SMB2 requests + * in order by SMB2 command. This is similar to "wct" in SMB/CIFS requests. + * + * Note that commands are defined in smb2pdu.h in le16 but the array below is + * indexed by command in host byte order + */ +static const __le16 smb2_req_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = { + /* SMB2_NEGOTIATE */ cpu_to_le16(36), + /* SMB2_SESSION_SETUP */ cpu_to_le16(25), + /* SMB2_LOGOFF */ cpu_to_le16(4), + /* SMB2_TREE_CONNECT */ cpu_to_le16(9), + /* SMB2_TREE_DISCONNECT */ cpu_to_le16(4), + /* SMB2_CREATE */ cpu_to_le16(57), + /* SMB2_CLOSE */ cpu_to_le16(24), + /* SMB2_FLUSH */ cpu_to_le16(24), + /* SMB2_READ */ cpu_to_le16(49), + /* SMB2_WRITE */ cpu_to_le16(49), + /* SMB2_LOCK */ cpu_to_le16(48), + /* SMB2_IOCTL */ cpu_to_le16(57), + /* SMB2_CANCEL */ cpu_to_le16(4), + /* SMB2_ECHO */ cpu_to_le16(4), + /* SMB2_QUERY_DIRECTORY */ cpu_to_le16(33), + /* SMB2_CHANGE_NOTIFY */ cpu_to_le16(32), + /* SMB2_QUERY_INFO */ cpu_to_le16(41), + /* SMB2_SET_INFO */ cpu_to_le16(33), + /* use 44 for lease break */ + /* SMB2_OPLOCK_BREAK */ cpu_to_le16(36) +}; + +/* + * The size of the variable area depends on the offset and length fields + * located in different fields for various SMB2 requests. SMB2 requests + * with no variable length info, show an offset of zero for the offset field. + */ +static const bool has_smb2_data_area[NUMBER_OF_SMB2_COMMANDS] = { + /* SMB2_NEGOTIATE */ true, + /* SMB2_SESSION_SETUP */ true, + /* SMB2_LOGOFF */ false, + /* SMB2_TREE_CONNECT */ true, + /* SMB2_TREE_DISCONNECT */ false, + /* SMB2_CREATE */ true, + /* SMB2_CLOSE */ false, + /* SMB2_FLUSH */ false, + /* SMB2_READ */ true, + /* SMB2_WRITE */ true, + /* SMB2_LOCK */ true, + /* SMB2_IOCTL */ true, + /* SMB2_CANCEL */ false, /* BB CHECK this not listed in documentation */ + /* SMB2_ECHO */ false, + /* SMB2_QUERY_DIRECTORY */ true, + /* SMB2_CHANGE_NOTIFY */ false, + /* SMB2_QUERY_INFO */ true, + /* SMB2_SET_INFO */ true, + /* SMB2_OPLOCK_BREAK */ false +}; + +/* + * Set length of the data area and the offset to arguments. + * if they are invalid, return error. + */ +static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, + struct smb2_hdr *hdr) +{ + int ret = 0; + + *off = 0; + *len = 0; + + /* + * Following commands have data areas so we have to get the location + * of the data buffer offset and data buffer length for the particular + * command. + */ + switch (hdr->Command) { + case SMB2_SESSION_SETUP: + *off = le16_to_cpu(((struct smb2_sess_setup_req *)hdr)->SecurityBufferOffset); + *len = le16_to_cpu(((struct smb2_sess_setup_req *)hdr)->SecurityBufferLength); + break; + case SMB2_TREE_CONNECT: + *off = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathOffset); + *len = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathLength); + break; + case SMB2_CREATE: + { + if (((struct smb2_create_req *)hdr)->CreateContextsLength) { + *off = le32_to_cpu(((struct smb2_create_req *) + hdr)->CreateContextsOffset); + *len = le32_to_cpu(((struct smb2_create_req *) + hdr)->CreateContextsLength); + break; + } + + *off = le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset); + *len = le16_to_cpu(((struct smb2_create_req *)hdr)->NameLength); + break; + } + case SMB2_QUERY_INFO: + *off = le16_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferOffset); + *len = le32_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferLength); + break; + case SMB2_SET_INFO: + *off = le16_to_cpu(((struct smb2_set_info_req *)hdr)->BufferOffset); + *len = le32_to_cpu(((struct smb2_set_info_req *)hdr)->BufferLength); + break; + case SMB2_READ: + *off = le16_to_cpu(((struct smb2_read_req *)hdr)->ReadChannelInfoOffset); + *len = le16_to_cpu(((struct smb2_read_req *)hdr)->ReadChannelInfoLength); + break; + case SMB2_WRITE: + if (((struct smb2_write_req *)hdr)->DataOffset || + ((struct smb2_write_req *)hdr)->Length) { + *off = max_t(unsigned int, + le16_to_cpu(((struct smb2_write_req *)hdr)->DataOffset), + offsetof(struct smb2_write_req, Buffer)); + *len = le32_to_cpu(((struct smb2_write_req *)hdr)->Length); + break; + } + + *off = le16_to_cpu(((struct smb2_write_req *)hdr)->WriteChannelInfoOffset); + *len = le16_to_cpu(((struct smb2_write_req *)hdr)->WriteChannelInfoLength); + break; + case SMB2_QUERY_DIRECTORY: + *off = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameOffset); + *len = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameLength); + break; + case SMB2_LOCK: + { + int lock_count; + + /* + * smb2_lock request size is 48 included single + * smb2_lock_element structure size. + */ + lock_count = le16_to_cpu(((struct smb2_lock_req *)hdr)->LockCount) - 1; + if (lock_count > 0) { + *off = __SMB2_HEADER_STRUCTURE_SIZE + 48; + *len = sizeof(struct smb2_lock_element) * lock_count; + } + break; + } + case SMB2_IOCTL: + *off = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputOffset); + *len = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputCount); + break; + default: + ksmbd_debug(SMB, "no length check for command\n"); + break; + } + + if (*off > 4096) { + ksmbd_debug(SMB, "offset %d too large\n", *off); + ret = -EINVAL; + } else if ((u64)*off + *len > MAX_STREAM_PROT_LEN) { + ksmbd_debug(SMB, "Request is larger than maximum stream protocol length(%u): %llu\n", + MAX_STREAM_PROT_LEN, (u64)*off + *len); + ret = -EINVAL; + } + + return ret; +} + +/* + * Calculate the size of the SMB message based on the fixed header + * portion, the number of word parameters and the data portion of the message. + */ +static int smb2_calc_size(void *buf, unsigned int *len) +{ + struct smb2_pdu *pdu = (struct smb2_pdu *)buf; + struct smb2_hdr *hdr = &pdu->hdr; + unsigned int offset; /* the offset from the beginning of SMB to data area */ + unsigned int data_length; /* the length of the variable length data area */ + int ret; + + /* Structure Size has already been checked to make sure it is 64 */ + *len = le16_to_cpu(hdr->StructureSize); + + /* + * StructureSize2, ie length of fixed parameter area has already + * been checked to make sure it is the correct length. + */ + *len += le16_to_cpu(pdu->StructureSize2); + /* + * StructureSize2 of smb2_lock pdu is set to 48, indicating + * the size of smb2 lock request with single smb2_lock_element + * regardless of number of locks. Subtract single + * smb2_lock_element for correct buffer size check. + */ + if (hdr->Command == SMB2_LOCK) + *len -= sizeof(struct smb2_lock_element); + + if (has_smb2_data_area[le16_to_cpu(hdr->Command)] == false) + goto calc_size_exit; + + ret = smb2_get_data_area_len(&offset, &data_length, hdr); + if (ret) + return ret; + ksmbd_debug(SMB, "SMB2 data length %u offset %u\n", data_length, + offset); + + if (data_length > 0) { + /* + * Check to make sure that data area begins after fixed area, + * Note that last byte of the fixed area is part of data area + * for some commands, typically those with odd StructureSize, + * so we must add one to the calculation. + */ + if (offset + 1 < *len) { + ksmbd_debug(SMB, + "data area offset %d overlaps SMB2 header %u\n", + offset + 1, *len); + return -EINVAL; + } + + *len = offset + data_length; + } + +calc_size_exit: + ksmbd_debug(SMB, "SMB2 len %u\n", *len); + return 0; +} + +static inline int smb2_query_info_req_len(struct smb2_query_info_req *h) +{ + return le32_to_cpu(h->InputBufferLength) + + le32_to_cpu(h->OutputBufferLength); +} + +static inline int smb2_set_info_req_len(struct smb2_set_info_req *h) +{ + return le32_to_cpu(h->BufferLength); +} + +static inline int smb2_read_req_len(struct smb2_read_req *h) +{ + return le32_to_cpu(h->Length); +} + +static inline int smb2_write_req_len(struct smb2_write_req *h) +{ + return le32_to_cpu(h->Length); +} + +static inline int smb2_query_dir_req_len(struct smb2_query_directory_req *h) +{ + return le32_to_cpu(h->OutputBufferLength); +} + +static inline int smb2_ioctl_req_len(struct smb2_ioctl_req *h) +{ + return le32_to_cpu(h->InputCount) + + le32_to_cpu(h->OutputCount); +} + +static inline int smb2_ioctl_resp_len(struct smb2_ioctl_req *h) +{ + return le32_to_cpu(h->MaxInputResponse) + + le32_to_cpu(h->MaxOutputResponse); +} + +static int smb2_validate_credit_charge(struct ksmbd_conn *conn, + struct smb2_hdr *hdr) +{ + unsigned int req_len = 0, expect_resp_len = 0, calc_credit_num, max_len; + unsigned short credit_charge = le16_to_cpu(hdr->CreditCharge); + void *__hdr = hdr; + int ret = 0; + + switch (hdr->Command) { + case SMB2_QUERY_INFO: + req_len = smb2_query_info_req_len(__hdr); + break; + case SMB2_SET_INFO: + req_len = smb2_set_info_req_len(__hdr); + break; + case SMB2_READ: + req_len = smb2_read_req_len(__hdr); + break; + case SMB2_WRITE: + req_len = smb2_write_req_len(__hdr); + break; + case SMB2_QUERY_DIRECTORY: + req_len = smb2_query_dir_req_len(__hdr); + break; + case SMB2_IOCTL: + req_len = smb2_ioctl_req_len(__hdr); + expect_resp_len = smb2_ioctl_resp_len(__hdr); + break; + case SMB2_CANCEL: + return 0; + default: + req_len = 1; + break; + } + + credit_charge = max_t(unsigned short, credit_charge, 1); + max_len = max_t(unsigned int, req_len, expect_resp_len); + calc_credit_num = DIV_ROUND_UP(max_len, SMB2_MAX_BUFFER_SIZE); + + if (credit_charge < calc_credit_num) { + ksmbd_debug(SMB, "Insufficient credit charge, given: %d, needed: %d\n", + credit_charge, calc_credit_num); + return 1; + } else if (credit_charge > conn->vals->max_credits) { + ksmbd_debug(SMB, "Too large credit charge: %d\n", credit_charge); + return 1; + } + + spin_lock(&conn->credits_lock); + if (credit_charge > conn->total_credits) { + ksmbd_debug(SMB, "Insufficient credits granted, given: %u, granted: %u\n", + credit_charge, conn->total_credits); + ret = 1; + } + + if ((u64)conn->outstanding_credits + credit_charge > conn->total_credits) { + ksmbd_debug(SMB, "Limits exceeding the maximum allowable outstanding requests, given : %u, pending : %u\n", + credit_charge, conn->outstanding_credits); + ret = 1; + } else + conn->outstanding_credits += credit_charge; + + spin_unlock(&conn->credits_lock); + + return ret; +} + +int ksmbd_smb2_check_message(struct ksmbd_work *work) +{ + struct smb2_pdu *pdu = ksmbd_req_buf_next(work); + struct smb2_hdr *hdr = &pdu->hdr; + int command; + __u32 clc_len; /* calculated length */ + __u32 len = get_rfc1002_len(work->request_buf); + + if (le32_to_cpu(hdr->NextCommand) > 0) + len = le32_to_cpu(hdr->NextCommand); + else if (work->next_smb2_rcv_hdr_off) + len -= work->next_smb2_rcv_hdr_off; + + if (check_smb2_hdr(hdr)) + return 1; + + if (hdr->StructureSize != SMB2_HEADER_STRUCTURE_SIZE) { + ksmbd_debug(SMB, "Illegal structure size %u\n", + le16_to_cpu(hdr->StructureSize)); + return 1; + } + + command = le16_to_cpu(hdr->Command); + if (command >= NUMBER_OF_SMB2_COMMANDS) { + ksmbd_debug(SMB, "Illegal SMB2 command %d\n", command); + return 1; + } + + if (smb2_req_struct_sizes[command] != pdu->StructureSize2) { + if (command != SMB2_OPLOCK_BREAK_HE && + (hdr->Status == 0 || pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2_LE)) { + /* error packets have 9 byte structure size */ + ksmbd_debug(SMB, + "Illegal request size %u for command %d\n", + le16_to_cpu(pdu->StructureSize2), command); + return 1; + } else if (command == SMB2_OPLOCK_BREAK_HE && + hdr->Status == 0 && + le16_to_cpu(pdu->StructureSize2) != OP_BREAK_STRUCT_SIZE_20 && + le16_to_cpu(pdu->StructureSize2) != OP_BREAK_STRUCT_SIZE_21) { + /* special case for SMB2.1 lease break message */ + ksmbd_debug(SMB, + "Illegal request size %d for oplock break\n", + le16_to_cpu(pdu->StructureSize2)); + return 1; + } + } + + if (smb2_calc_size(hdr, &clc_len)) + return 1; + + if (len != clc_len) { + /* client can return one byte more due to implied bcc[0] */ + if (clc_len == len + 1) + goto validate_credit; + + /* + * Some windows servers (win2016) will pad also the final + * PDU in a compound to 8 bytes. + */ + if (ALIGN(clc_len, 8) == len) + goto validate_credit; + + /* + * windows client also pad up to 8 bytes when compounding. + * If pad is longer than eight bytes, log the server behavior + * (once), since may indicate a problem but allow it and + * continue since the frame is parseable. + */ + if (clc_len < len) { + ksmbd_debug(SMB, + "cli req padded more than expected. Length %d not %d for cmd:%d mid:%llu\n", + len, clc_len, command, + le64_to_cpu(hdr->MessageId)); + goto validate_credit; + } + + ksmbd_debug(SMB, + "cli req too short, len %d not %d. cmd:%d mid:%llu\n", + len, clc_len, command, + le64_to_cpu(hdr->MessageId)); + + return 1; + } + +validate_credit: + if ((work->conn->vals->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) && + smb2_validate_credit_charge(work->conn, hdr)) { + work->conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); + return 1; + } + + return 0; +} + +int smb2_negotiate_request(struct ksmbd_work *work) +{ + return ksmbd_smb_negotiate_common(work, SMB2_NEGOTIATE_HE); +} diff --git a/fs/ksmbd/smb2ops.c b/fs/ksmbd/smb2ops.c new file mode 100644 index 0000000000000..8c26b11c33bb5 --- /dev/null +++ b/fs/ksmbd/smb2ops.c @@ -0,0 +1,367 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include "glob.h" +#include "smb2pdu.h" + +#include "auth.h" +#include "connection.h" +#include "smb_common.h" +#include "server.h" + +#ifdef CONFIG_SMB_INSECURE_SERVER +static struct smb_version_values smb20_server_values = { + .version_string = SMB20_VERSION_STRING, + .protocol_id = SMB20_PROT_ID, + .capabilities = 0, + .max_read_size = CIFS_DEFAULT_IOSIZE, + .max_write_size = CIFS_DEFAULT_IOSIZE, + .max_trans_size = CIFS_DEFAULT_IOSIZE, + .max_credits = SMB2_MAX_CREDITS, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .create_lease_size = sizeof(struct create_lease), + .create_durable_size = sizeof(struct create_durable_rsp), + .create_mxac_size = sizeof(struct create_mxac_rsp), + .create_disk_id_size = sizeof(struct create_disk_id_rsp), + .create_posix_size = sizeof(struct create_posix_rsp), +}; +#endif + +static struct smb_version_values smb21_server_values = { + .version_string = SMB21_VERSION_STRING, + .protocol_id = SMB21_PROT_ID, + .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, + .max_read_size = SMB21_DEFAULT_IOSIZE, + .max_write_size = SMB21_DEFAULT_IOSIZE, + .max_trans_size = SMB21_DEFAULT_IOSIZE, + .max_credits = SMB2_MAX_CREDITS, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .create_lease_size = sizeof(struct create_lease), + .create_durable_size = sizeof(struct create_durable_rsp), + .create_mxac_size = sizeof(struct create_mxac_rsp), + .create_disk_id_size = sizeof(struct create_disk_id_rsp), + .create_posix_size = sizeof(struct create_posix_rsp), +}; + +static struct smb_version_values smb30_server_values = { + .version_string = SMB30_VERSION_STRING, + .protocol_id = SMB30_PROT_ID, + .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, + .max_read_size = SMB3_DEFAULT_IOSIZE, + .max_write_size = SMB3_DEFAULT_IOSIZE, + .max_trans_size = SMB3_DEFAULT_TRANS_SIZE, + .max_credits = SMB2_MAX_CREDITS, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .create_lease_size = sizeof(struct create_lease_v2), + .create_durable_size = sizeof(struct create_durable_rsp), + .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), + .create_mxac_size = sizeof(struct create_mxac_rsp), + .create_disk_id_size = sizeof(struct create_disk_id_rsp), + .create_posix_size = sizeof(struct create_posix_rsp), +}; + +static struct smb_version_values smb302_server_values = { + .version_string = SMB302_VERSION_STRING, + .protocol_id = SMB302_PROT_ID, + .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, + .max_read_size = SMB3_DEFAULT_IOSIZE, + .max_write_size = SMB3_DEFAULT_IOSIZE, + .max_trans_size = SMB3_DEFAULT_TRANS_SIZE, + .max_credits = SMB2_MAX_CREDITS, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .create_lease_size = sizeof(struct create_lease_v2), + .create_durable_size = sizeof(struct create_durable_rsp), + .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), + .create_mxac_size = sizeof(struct create_mxac_rsp), + .create_disk_id_size = sizeof(struct create_disk_id_rsp), + .create_posix_size = sizeof(struct create_posix_rsp), +}; + +static struct smb_version_values smb311_server_values = { + .version_string = SMB311_VERSION_STRING, + .protocol_id = SMB311_PROT_ID, + .capabilities = SMB2_GLOBAL_CAP_LARGE_MTU, + .max_read_size = SMB3_DEFAULT_IOSIZE, + .max_write_size = SMB3_DEFAULT_IOSIZE, + .max_trans_size = SMB3_DEFAULT_TRANS_SIZE, + .max_credits = SMB2_MAX_CREDITS, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE, + .shared_lock_type = SMB2_LOCKFLAG_SHARED, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .create_lease_size = sizeof(struct create_lease_v2), + .create_durable_size = sizeof(struct create_durable_rsp), + .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), + .create_mxac_size = sizeof(struct create_mxac_rsp), + .create_disk_id_size = sizeof(struct create_disk_id_rsp), + .create_posix_size = sizeof(struct create_posix_rsp), +}; + +static struct smb_version_ops smb2_0_server_ops = { + .get_cmd_val = get_smb2_cmd_val, + .init_rsp_hdr = init_smb2_rsp_hdr, + .set_rsp_status = set_smb2_rsp_status, + .allocate_rsp_buf = smb2_allocate_rsp_buf, + .set_rsp_credits = smb2_set_rsp_credits, + .check_user_session = smb2_check_user_session, + .get_ksmbd_tcon = smb2_get_ksmbd_tcon, + .is_sign_req = smb2_is_sign_req, + .check_sign_req = smb2_check_sign_req, + .set_sign_rsp = smb2_set_sign_rsp +}; + +static struct smb_version_ops smb3_0_server_ops = { + .get_cmd_val = get_smb2_cmd_val, + .init_rsp_hdr = init_smb2_rsp_hdr, + .set_rsp_status = set_smb2_rsp_status, + .allocate_rsp_buf = smb2_allocate_rsp_buf, + .set_rsp_credits = smb2_set_rsp_credits, + .check_user_session = smb2_check_user_session, + .get_ksmbd_tcon = smb2_get_ksmbd_tcon, + .is_sign_req = smb2_is_sign_req, + .check_sign_req = smb3_check_sign_req, + .set_sign_rsp = smb3_set_sign_rsp, + .generate_signingkey = ksmbd_gen_smb30_signingkey, + .generate_encryptionkey = ksmbd_gen_smb30_encryptionkey, + .is_transform_hdr = smb3_is_transform_hdr, + .decrypt_req = smb3_decrypt_req, + .encrypt_resp = smb3_encrypt_resp +}; + +static struct smb_version_ops smb3_11_server_ops = { + .get_cmd_val = get_smb2_cmd_val, + .init_rsp_hdr = init_smb2_rsp_hdr, + .set_rsp_status = set_smb2_rsp_status, + .allocate_rsp_buf = smb2_allocate_rsp_buf, + .set_rsp_credits = smb2_set_rsp_credits, + .check_user_session = smb2_check_user_session, + .get_ksmbd_tcon = smb2_get_ksmbd_tcon, + .is_sign_req = smb2_is_sign_req, + .check_sign_req = smb3_check_sign_req, + .set_sign_rsp = smb3_set_sign_rsp, + .generate_signingkey = ksmbd_gen_smb311_signingkey, + .generate_encryptionkey = ksmbd_gen_smb311_encryptionkey, + .is_transform_hdr = smb3_is_transform_hdr, + .decrypt_req = smb3_decrypt_req, + .encrypt_resp = smb3_encrypt_resp +}; + +static struct smb_version_cmds smb2_0_server_cmds[NUMBER_OF_SMB2_COMMANDS] = { + [SMB2_NEGOTIATE_HE] = { .proc = smb2_negotiate_request, }, + [SMB2_SESSION_SETUP_HE] = { .proc = smb2_sess_setup, }, + [SMB2_TREE_CONNECT_HE] = { .proc = smb2_tree_connect,}, + [SMB2_TREE_DISCONNECT_HE] = { .proc = smb2_tree_disconnect,}, + [SMB2_LOGOFF_HE] = { .proc = smb2_session_logoff,}, + [SMB2_CREATE_HE] = { .proc = smb2_open}, + [SMB2_QUERY_INFO_HE] = { .proc = smb2_query_info}, + [SMB2_QUERY_DIRECTORY_HE] = { .proc = smb2_query_dir}, + [SMB2_CLOSE_HE] = { .proc = smb2_close}, + [SMB2_ECHO_HE] = { .proc = smb2_echo}, + [SMB2_SET_INFO_HE] = { .proc = smb2_set_info}, + [SMB2_READ_HE] = { .proc = smb2_read}, + [SMB2_WRITE_HE] = { .proc = smb2_write}, + [SMB2_FLUSH_HE] = { .proc = smb2_flush}, + [SMB2_CANCEL_HE] = { .proc = smb2_cancel}, + [SMB2_LOCK_HE] = { .proc = smb2_lock}, + [SMB2_IOCTL_HE] = { .proc = smb2_ioctl}, + [SMB2_OPLOCK_BREAK_HE] = { .proc = smb2_oplock_break}, + [SMB2_CHANGE_NOTIFY_HE] = { .proc = smb2_notify}, +}; + +#ifdef CONFIG_SMB_INSECURE_SERVER +/** + * init_smb2_0_server() - initialize a smb server connection with smb2.0 + * command dispatcher + * @conn: connection instance + */ +int init_smb2_0_server(struct ksmbd_conn *conn) +{ + conn->vals = &smb20_server_values; + conn->ops = &smb2_0_server_ops; + conn->cmds = smb2_0_server_cmds; + conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); + conn->signing_algorithm = SIGNING_ALG_HMAC_SHA256; + return 0; +} +#else +int init_smb2_0_server(struct ksmbd_conn *conn) +{ + return -EOPNOTSUPP; +} +#endif + +/** + * init_smb2_1_server() - initialize a smb server connection with smb2.1 + * command dispatcher + * @conn: connection instance + */ +void init_smb2_1_server(struct ksmbd_conn *conn) +{ + conn->vals = &smb21_server_values; + conn->ops = &smb2_0_server_ops; + conn->cmds = smb2_0_server_cmds; + conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); + conn->signing_algorithm = SIGNING_ALG_HMAC_SHA256; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; +} + +/** + * init_smb3_0_server() - initialize a smb server connection with smb3.0 + * command dispatcher + * @conn: connection instance + */ +void init_smb3_0_server(struct ksmbd_conn *conn) +{ + conn->vals = &smb30_server_values; + conn->ops = &smb3_0_server_ops; + conn->cmds = smb2_0_server_cmds; + conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); + conn->signing_algorithm = SIGNING_ALG_AES_CMAC; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION || + (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) && + conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION)) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; +} + +/** + * init_smb3_02_server() - initialize a smb server connection with smb3.02 + * command dispatcher + * @conn: connection instance + */ +void init_smb3_02_server(struct ksmbd_conn *conn) +{ + conn->vals = &smb302_server_values; + conn->ops = &smb3_0_server_ops; + conn->cmds = smb2_0_server_cmds; + conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); + conn->signing_algorithm = SIGNING_ALG_AES_CMAC; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION || + (!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) && + conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION)) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; +} + +/** + * init_smb3_11_server() - initialize a smb server connection with smb3.11 + * command dispatcher + * @conn: connection instance + */ +int init_smb3_11_server(struct ksmbd_conn *conn) +{ + conn->vals = &smb311_server_values; + conn->ops = &smb3_11_server_ops; + conn->cmds = smb2_0_server_cmds; + conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds); + conn->signing_algorithm = SIGNING_ALG_AES_CMAC; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING; + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) + conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL; + + INIT_LIST_HEAD(&conn->preauth_sess_table); + return 0; +} + +void init_smb2_max_read_size(unsigned int sz) +{ + sz = clamp_val(sz, SMB3_MIN_IOSIZE, SMB3_MAX_IOSIZE); + smb21_server_values.max_read_size = sz; + smb30_server_values.max_read_size = sz; + smb302_server_values.max_read_size = sz; + smb311_server_values.max_read_size = sz; +} + +void init_smb2_max_write_size(unsigned int sz) +{ + sz = clamp_val(sz, SMB3_MIN_IOSIZE, SMB3_MAX_IOSIZE); + smb21_server_values.max_write_size = sz; + smb30_server_values.max_write_size = sz; + smb302_server_values.max_write_size = sz; + smb311_server_values.max_write_size = sz; +} + +void init_smb2_max_trans_size(unsigned int sz) +{ + sz = clamp_val(sz, SMB3_MIN_IOSIZE, SMB3_MAX_IOSIZE); + smb21_server_values.max_trans_size = sz; + smb30_server_values.max_trans_size = sz; + smb302_server_values.max_trans_size = sz; + smb311_server_values.max_trans_size = sz; +} + +void init_smb2_max_credits(unsigned int sz) +{ + smb21_server_values.max_credits = sz; + smb30_server_values.max_credits = sz; + smb302_server_values.max_credits = sz; + smb311_server_values.max_credits = sz; +} diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c new file mode 100644 index 0000000000000..82c20bce6101f --- /dev/null +++ b/fs/ksmbd/smb2pdu.c @@ -0,0 +1,8799 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "glob.h" +#include "smb2pdu.h" +#include "smbfsctl.h" +#include "oplock.h" +#include "smbacl.h" + +#include "auth.h" +#include "asn1.h" +#include "connection.h" +#include "transport_ipc.h" +#include "transport_rdma.h" +#include "vfs.h" +#include "vfs_cache.h" +#include "misc.h" + +#include "server.h" +#include "smb_common.h" +#include "smbstatus.h" +#include "ksmbd_work.h" +#include "mgmt/user_config.h" +#include "mgmt/share_config.h" +#include "mgmt/tree_connect.h" +#include "mgmt/user_session.h" +#include "mgmt/ksmbd_ida.h" +#include "ndr.h" + +static void __wbuf(struct ksmbd_work *work, void **req, void **rsp) +{ + if (work->next_smb2_rcv_hdr_off) { + *req = ksmbd_req_buf_next(work); + *rsp = ksmbd_resp_buf_next(work); + } else { + *req = smb2_get_msg(work->request_buf); + *rsp = smb2_get_msg(work->response_buf); + } +} + +#define WORK_BUFFERS(w, rq, rs) __wbuf((w), (void **)&(rq), (void **)&(rs)) + +/** + * check_session_id() - check for valid session id in smb header + * @conn: connection instance + * @id: session id from smb header + * + * Return: 1 if valid session id, otherwise 0 + */ +static inline bool check_session_id(struct ksmbd_conn *conn, u64 id) +{ + struct ksmbd_session *sess; + + if (id == 0 || id == -1) + return false; + + sess = ksmbd_session_lookup_all(conn, id); + if (sess) + return true; + pr_err("Invalid user session id: %llu\n", id); + return false; +} + +struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn *conn) +{ + return xa_load(&sess->ksmbd_chann_list, (long)conn); +} + +/** + * smb2_get_ksmbd_tcon() - get tree connection information using a tree id. + * @work: smb work + * + * Return: 0 if there is a tree connection matched or these are + * skipable commands, otherwise error + */ +int smb2_get_ksmbd_tcon(struct ksmbd_work *work) +{ + struct smb2_hdr *req_hdr = smb2_get_msg(work->request_buf); + unsigned int cmd = le16_to_cpu(req_hdr->Command); + int tree_id; + + work->tcon = NULL; + if (cmd == SMB2_TREE_CONNECT_HE || + cmd == SMB2_CANCEL_HE || + cmd == SMB2_LOGOFF_HE) { + ksmbd_debug(SMB, "skip to check tree connect request\n"); + return 0; + } + + if (xa_empty(&work->sess->tree_conns)) { + ksmbd_debug(SMB, "NO tree connected\n"); + return -ENOENT; + } + + tree_id = le32_to_cpu(req_hdr->Id.SyncId.TreeId); + work->tcon = ksmbd_tree_conn_lookup(work->sess, tree_id); + if (!work->tcon) { + pr_err("Invalid tid %d\n", tree_id); + return -EINVAL; + } + + return 1; +} + +/** + * smb2_set_err_rsp() - set error response code on smb response + * @work: smb work containing response buffer + */ +void smb2_set_err_rsp(struct ksmbd_work *work) +{ + struct smb2_err_rsp *err_rsp; + + if (work->next_smb2_rcv_hdr_off) + err_rsp = ksmbd_resp_buf_next(work); + else + err_rsp = smb2_get_msg(work->response_buf); + + if (err_rsp->hdr.Status != STATUS_STOPPED_ON_SYMLINK) { + err_rsp->StructureSize = SMB2_ERROR_STRUCTURE_SIZE2_LE; + err_rsp->ErrorContextCount = 0; + err_rsp->Reserved = 0; + err_rsp->ByteCount = 0; + err_rsp->ErrorData[0] = 0; + inc_rfc1001_len(work->response_buf, SMB2_ERROR_STRUCTURE_SIZE2); + } +} + +/** + * is_smb2_neg_cmd() - is it smb2 negotiation command + * @work: smb work containing smb header + * + * Return: true if smb2 negotiation command, otherwise false + */ +bool is_smb2_neg_cmd(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); + + /* is it SMB2 header ? */ + if (hdr->ProtocolId != SMB2_PROTO_NUMBER) + return false; + + /* make sure it is request not response message */ + if (hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR) + return false; + + if (hdr->Command != SMB2_NEGOTIATE) + return false; + + return true; +} + +/** + * is_smb2_rsp() - is it smb2 response + * @work: smb work containing smb response buffer + * + * Return: true if smb2 response, otherwise false + */ +bool is_smb2_rsp(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr = smb2_get_msg(work->response_buf); + + /* is it SMB2 header ? */ + if (hdr->ProtocolId != SMB2_PROTO_NUMBER) + return false; + + /* make sure it is response not request message */ + if (!(hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR)) + return false; + + return true; +} + +/** + * get_smb2_cmd_val() - get smb command code from smb header + * @work: smb work containing smb request buffer + * + * Return: smb2 request command value + */ +u16 get_smb2_cmd_val(struct ksmbd_work *work) +{ + struct smb2_hdr *rcv_hdr; + + if (work->next_smb2_rcv_hdr_off) + rcv_hdr = ksmbd_req_buf_next(work); + else + rcv_hdr = smb2_get_msg(work->request_buf); + return le16_to_cpu(rcv_hdr->Command); +} + +/** + * set_smb2_rsp_status() - set error response code on smb2 header + * @work: smb work containing response buffer + * @err: error response code + */ +void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err) +{ + struct smb2_hdr *rsp_hdr; + + if (work->next_smb2_rcv_hdr_off) + rsp_hdr = ksmbd_resp_buf_next(work); + else + rsp_hdr = smb2_get_msg(work->response_buf); + rsp_hdr->Status = err; + smb2_set_err_rsp(work); +} + +/** + * init_smb2_neg_rsp() - initialize smb2 response for negotiate command + * @work: smb work containing smb request buffer + * + * smb2 negotiate response is sent in reply of smb1 negotiate command for + * dialect auto-negotiation. + */ +int init_smb2_neg_rsp(struct ksmbd_work *work) +{ + struct smb2_hdr *rsp_hdr; + struct smb2_negotiate_rsp *rsp; + struct ksmbd_conn *conn = work->conn; + + if (conn->need_neg == false) + return -EINVAL; + if (!(conn->dialect >= SMB20_PROT_ID && + conn->dialect <= SMB311_PROT_ID)) + return -EINVAL; + + *(__be32 *)work->response_buf = + cpu_to_be32(conn->vals->header_size); + + rsp_hdr = smb2_get_msg(work->response_buf); + memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); + rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->CreditRequest = cpu_to_le16(2); + rsp_hdr->Command = SMB2_NEGOTIATE; + rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); + rsp_hdr->NextCommand = 0; + rsp_hdr->MessageId = 0; + rsp_hdr->Id.SyncId.ProcessId = 0; + rsp_hdr->Id.SyncId.TreeId = 0; + rsp_hdr->SessionId = 0; + memset(rsp_hdr->Signature, 0, 16); + + rsp = smb2_get_msg(work->response_buf); + + WARN_ON(ksmbd_conn_good(work)); + + rsp->StructureSize = cpu_to_le16(65); + ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); + rsp->DialectRevision = cpu_to_le16(conn->dialect); + /* Not setting conn guid rsp->ServerGUID, as it + * not used by client for identifying connection + */ + rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); + /* Default Max Message Size till SMB2.0, 64K*/ + rsp->MaxTransactSize = cpu_to_le32(conn->vals->max_trans_size); + rsp->MaxReadSize = cpu_to_le32(conn->vals->max_read_size); + rsp->MaxWriteSize = cpu_to_le32(conn->vals->max_write_size); + + rsp->SystemTime = cpu_to_le64(ksmbd_systime()); + rsp->ServerStartTime = 0; + + rsp->SecurityBufferOffset = cpu_to_le16(128); + rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH); + ksmbd_copy_gss_neg_header((char *)(&rsp->hdr) + + le16_to_cpu(rsp->SecurityBufferOffset)); + inc_rfc1001_len(work->response_buf, + sizeof(struct smb2_negotiate_rsp) - + sizeof(struct smb2_hdr) - sizeof(rsp->Buffer) + + AUTH_GSS_LENGTH); + rsp->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED_LE; + if (server_conf.signing == KSMBD_CONFIG_OPT_MANDATORY) + rsp->SecurityMode |= SMB2_NEGOTIATE_SIGNING_REQUIRED_LE; + conn->use_spnego = true; + + ksmbd_conn_set_need_negotiate(work); + return 0; +} + +/** + * smb2_set_rsp_credits() - set number of credits in response buffer + * @work: smb work containing smb response buffer + */ +int smb2_set_rsp_credits(struct ksmbd_work *work) +{ + struct smb2_hdr *req_hdr = ksmbd_req_buf_next(work); + struct smb2_hdr *hdr = ksmbd_resp_buf_next(work); + struct ksmbd_conn *conn = work->conn; + unsigned short credits_requested, aux_max; + unsigned short credit_charge, credits_granted = 0; + + if (work->send_no_response) + return 0; + + hdr->CreditCharge = req_hdr->CreditCharge; + + if (conn->total_credits > conn->vals->max_credits) { + hdr->CreditRequest = 0; + pr_err("Total credits overflow: %d\n", conn->total_credits); + return -EINVAL; + } + + credit_charge = max_t(unsigned short, + le16_to_cpu(req_hdr->CreditCharge), 1); + if (credit_charge > conn->total_credits) { + ksmbd_debug(SMB, "Insufficient credits granted, given: %u, granted: %u\n", + credit_charge, conn->total_credits); + return -EINVAL; + } + + conn->total_credits -= credit_charge; + conn->outstanding_credits -= credit_charge; + credits_requested = max_t(unsigned short, + le16_to_cpu(req_hdr->CreditRequest), 1); + + /* according to smb2.credits smbtorture, Windows server + * 2016 or later grant up to 8192 credits at once. + * + * TODO: Need to adjuct CreditRequest value according to + * current cpu load + */ + if (hdr->Command == SMB2_NEGOTIATE) + aux_max = 1; + else + aux_max = conn->vals->max_credits - credit_charge; + credits_granted = min_t(unsigned short, credits_requested, aux_max); + + if (conn->vals->max_credits - conn->total_credits < credits_granted) + credits_granted = conn->vals->max_credits - + conn->total_credits; + + conn->total_credits += credits_granted; + work->credits_granted += credits_granted; + + if (!req_hdr->NextCommand) { + /* Update CreditRequest in last request */ + hdr->CreditRequest = cpu_to_le16(work->credits_granted); + } + ksmbd_debug(SMB, + "credits: requested[%d] granted[%d] total_granted[%d]\n", + credits_requested, credits_granted, + conn->total_credits); + return 0; +} + +/** + * init_chained_smb2_rsp() - initialize smb2 chained response + * @work: smb work containing smb response buffer + */ +static void init_chained_smb2_rsp(struct ksmbd_work *work) +{ + struct smb2_hdr *req = ksmbd_req_buf_next(work); + struct smb2_hdr *rsp = ksmbd_resp_buf_next(work); + struct smb2_hdr *rsp_hdr; + struct smb2_hdr *rcv_hdr; + int next_hdr_offset = 0; + int len, new_len; + + /* Len of this response = updated RFC len - offset of previous cmd + * in the compound rsp + */ + + /* Storing the current local FID which may be needed by subsequent + * command in the compound request + */ + if (req->Command == SMB2_CREATE && rsp->Status == STATUS_SUCCESS) { + work->compound_fid = ((struct smb2_create_rsp *)rsp)->VolatileFileId; + work->compound_pfid = ((struct smb2_create_rsp *)rsp)->PersistentFileId; + work->compound_sid = le64_to_cpu(rsp->SessionId); + } + + len = get_rfc1002_len(work->response_buf) - work->next_smb2_rsp_hdr_off; + next_hdr_offset = le32_to_cpu(req->NextCommand); + + new_len = ALIGN(len, 8); + inc_rfc1001_len(work->response_buf, + sizeof(struct smb2_hdr) + new_len - len); + rsp->NextCommand = cpu_to_le32(new_len); + + work->next_smb2_rcv_hdr_off += next_hdr_offset; + work->next_smb2_rsp_hdr_off += new_len; + ksmbd_debug(SMB, + "Compound req new_len = %d rcv off = %d rsp off = %d\n", + new_len, work->next_smb2_rcv_hdr_off, + work->next_smb2_rsp_hdr_off); + + rsp_hdr = ksmbd_resp_buf_next(work); + rcv_hdr = ksmbd_req_buf_next(work); + + if (!(rcv_hdr->Flags & SMB2_FLAGS_RELATED_OPERATIONS)) { + ksmbd_debug(SMB, "related flag should be set\n"); + work->compound_fid = KSMBD_NO_FID; + work->compound_pfid = KSMBD_NO_FID; + } + memset((char *)rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); + rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->Command = rcv_hdr->Command; + + /* + * Message is response. We don't grant oplock yet. + */ + rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR | + SMB2_FLAGS_RELATED_OPERATIONS); + rsp_hdr->NextCommand = 0; + rsp_hdr->MessageId = rcv_hdr->MessageId; + rsp_hdr->Id.SyncId.ProcessId = rcv_hdr->Id.SyncId.ProcessId; + rsp_hdr->Id.SyncId.TreeId = rcv_hdr->Id.SyncId.TreeId; + rsp_hdr->SessionId = rcv_hdr->SessionId; + memcpy(rsp_hdr->Signature, rcv_hdr->Signature, 16); +} + +/** + * is_chained_smb2_message() - check for chained command + * @work: smb work containing smb request buffer + * + * Return: true if chained request, otherwise false + */ +bool is_chained_smb2_message(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); + unsigned int len, next_cmd; + + if (hdr->ProtocolId != SMB2_PROTO_NUMBER) + return false; + + hdr = ksmbd_req_buf_next(work); + next_cmd = le32_to_cpu(hdr->NextCommand); + if (next_cmd > 0) { + if ((u64)work->next_smb2_rcv_hdr_off + next_cmd + + __SMB2_HEADER_STRUCTURE_SIZE > + get_rfc1002_len(work->request_buf)) { + pr_err("next command(%u) offset exceeds smb msg size\n", + next_cmd); + return false; + } + + if ((u64)get_rfc1002_len(work->response_buf) + MAX_CIFS_SMALL_BUFFER_SIZE > + work->response_sz) { + pr_err("next response offset exceeds response buffer size\n"); + return false; + } + + ksmbd_debug(SMB, "got SMB2 chained command\n"); + init_chained_smb2_rsp(work); + return true; + } else if (work->next_smb2_rcv_hdr_off) { + /* + * This is last request in chained command, + * align response to 8 byte + */ + len = ALIGN(get_rfc1002_len(work->response_buf), 8); + len = len - get_rfc1002_len(work->response_buf); + if (len) { + ksmbd_debug(SMB, "padding len %u\n", len); + inc_rfc1001_len(work->response_buf, len); + if (work->aux_payload_sz) + work->aux_payload_sz += len; + } + } + return false; +} + +/** + * init_smb2_rsp_hdr() - initialize smb2 response + * @work: smb work containing smb request buffer + * + * Return: 0 + */ +int init_smb2_rsp_hdr(struct ksmbd_work *work) +{ + struct smb2_hdr *rsp_hdr = smb2_get_msg(work->response_buf); + struct smb2_hdr *rcv_hdr = smb2_get_msg(work->request_buf); + struct ksmbd_conn *conn = work->conn; + + memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); + *(__be32 *)work->response_buf = + cpu_to_be32(conn->vals->header_size); + rsp_hdr->ProtocolId = rcv_hdr->ProtocolId; + rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE; + rsp_hdr->Command = rcv_hdr->Command; + + /* + * Message is response. We don't grant oplock yet. + */ + rsp_hdr->Flags = (SMB2_FLAGS_SERVER_TO_REDIR); + rsp_hdr->NextCommand = 0; + rsp_hdr->MessageId = rcv_hdr->MessageId; + rsp_hdr->Id.SyncId.ProcessId = rcv_hdr->Id.SyncId.ProcessId; + rsp_hdr->Id.SyncId.TreeId = rcv_hdr->Id.SyncId.TreeId; + rsp_hdr->SessionId = rcv_hdr->SessionId; + memcpy(rsp_hdr->Signature, rcv_hdr->Signature, 16); + + work->synchronous = true; + if (work->async_id) { + ksmbd_release_id(&conn->async_ida, work->async_id); + work->async_id = 0; + } + + return 0; +} + +/** + * smb2_allocate_rsp_buf() - allocate smb2 response buffer + * @work: smb work containing smb request buffer + * + * Return: 0 on success, otherwise -ENOMEM + */ +int smb2_allocate_rsp_buf(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); + size_t small_sz = MAX_CIFS_SMALL_BUFFER_SIZE; + size_t large_sz = small_sz + work->conn->vals->max_trans_size; + size_t sz = small_sz; + int cmd = le16_to_cpu(hdr->Command); + + if (cmd == SMB2_IOCTL_HE || cmd == SMB2_QUERY_DIRECTORY_HE) + sz = large_sz; + + if (cmd == SMB2_QUERY_INFO_HE) { + struct smb2_query_info_req *req; + + req = smb2_get_msg(work->request_buf); + if ((req->InfoType == SMB2_O_INFO_FILE && + (req->FileInfoClass == FILE_FULL_EA_INFORMATION || + req->FileInfoClass == FILE_ALL_INFORMATION)) || + req->InfoType == SMB2_O_INFO_SECURITY) + sz = large_sz; + } + + /* allocate large response buf for chained commands */ + if (le32_to_cpu(hdr->NextCommand) > 0) + sz = large_sz; + + work->response_buf = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO); + if (!work->response_buf) + return -ENOMEM; + + work->response_sz = sz; + return 0; +} + +/** + * smb2_check_user_session() - check for valid session for a user + * @work: smb work containing smb request buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_check_user_session(struct ksmbd_work *work) +{ + struct smb2_hdr *req_hdr = smb2_get_msg(work->request_buf); + struct ksmbd_conn *conn = work->conn; + unsigned int cmd = conn->ops->get_cmd_val(work); + unsigned long long sess_id; + + work->sess = NULL; + /* + * SMB2_ECHO, SMB2_NEGOTIATE, SMB2_SESSION_SETUP command do not + * require a session id, so no need to validate user session's for + * these commands. + */ + if (cmd == SMB2_ECHO_HE || cmd == SMB2_NEGOTIATE_HE || + cmd == SMB2_SESSION_SETUP_HE) + return 0; + + if (!ksmbd_conn_good(work)) + return -EINVAL; + + sess_id = le64_to_cpu(req_hdr->SessionId); + /* Check for validity of user session */ + work->sess = ksmbd_session_lookup_all(conn, sess_id); + if (work->sess) + return 1; + ksmbd_debug(SMB, "Invalid user session, Uid %llu\n", sess_id); + return -EINVAL; +} + +static void destroy_previous_session(struct ksmbd_conn *conn, + struct ksmbd_user *user, u64 id) +{ + struct ksmbd_session *prev_sess = ksmbd_session_lookup_slowpath(id); + struct ksmbd_user *prev_user; + struct channel *chann; + long index; + + if (!prev_sess) + return; + + prev_user = prev_sess->user; + + if (!prev_user || + strcmp(user->name, prev_user->name) || + user->passkey_sz != prev_user->passkey_sz || + memcmp(user->passkey, prev_user->passkey, user->passkey_sz)) + return; + + prev_sess->state = SMB2_SESSION_EXPIRED; + xa_for_each(&prev_sess->ksmbd_chann_list, index, chann) + chann->conn->status = KSMBD_SESS_EXITING; +} + +/** + * smb2_get_name() - get filename string from on the wire smb format + * @share: ksmbd_share_config pointer + * @src: source buffer + * @maxlen: maxlen of source string + * @local_nls: nls_table pointer + * + * Return: matching converted filename on success, otherwise error ptr + */ +static char * +smb2_get_name(struct ksmbd_share_config *share, const char *src, + const int maxlen, struct nls_table *local_nls) +{ + char *name; + + name = smb_strndup_from_utf16(src, maxlen, 1, local_nls); + if (IS_ERR(name)) { + pr_err("failed to get name %ld\n", PTR_ERR(name)); + return name; + } + + ksmbd_conv_path_to_unix(name); + ksmbd_strip_last_slash(name); + return name; +} + +int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) +{ + struct smb2_hdr *rsp_hdr; + struct ksmbd_conn *conn = work->conn; + int id; + + rsp_hdr = smb2_get_msg(work->response_buf); + rsp_hdr->Flags |= SMB2_FLAGS_ASYNC_COMMAND; + + id = ksmbd_acquire_async_msg_id(&conn->async_ida); + if (id < 0) { + pr_err("Failed to alloc async message id\n"); + return id; + } + work->synchronous = false; + work->async_id = id; + rsp_hdr->Id.AsyncId = cpu_to_le64(id); + + ksmbd_debug(SMB, + "Send interim Response to inform async request id : %d\n", + work->async_id); + + work->cancel_fn = fn; + work->cancel_argv = arg; + + if (list_empty(&work->async_request_entry)) { + spin_lock(&conn->request_lock); + list_add_tail(&work->async_request_entry, &conn->async_requests); + spin_unlock(&conn->request_lock); + } + + return 0; +} + +void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status) +{ + struct smb2_hdr *rsp_hdr; + + rsp_hdr = smb2_get_msg(work->response_buf); + smb2_set_err_rsp(work); + rsp_hdr->Status = status; + + work->multiRsp = 1; + ksmbd_conn_write(work); + rsp_hdr->Status = 0; + work->multiRsp = 0; +} + +static __le32 smb2_get_reparse_tag_special_file(umode_t mode) +{ + if (S_ISDIR(mode) || S_ISREG(mode)) + return 0; + + if (S_ISLNK(mode)) + return IO_REPARSE_TAG_LX_SYMLINK_LE; + else if (S_ISFIFO(mode)) + return IO_REPARSE_TAG_LX_FIFO_LE; + else if (S_ISSOCK(mode)) + return IO_REPARSE_TAG_AF_UNIX_LE; + else if (S_ISCHR(mode)) + return IO_REPARSE_TAG_LX_CHR_LE; + else if (S_ISBLK(mode)) + return IO_REPARSE_TAG_LX_BLK_LE; + + return 0; +} + +/** + * smb2_get_dos_mode() - get file mode in dos format from unix mode + * @stat: kstat containing file mode + * @attribute: attribute flags + * + * Return: converted dos mode + */ +static int smb2_get_dos_mode(struct kstat *stat, int attribute) +{ + int attr = 0; + + if (S_ISDIR(stat->mode)) { + attr = ATTR_DIRECTORY | + (attribute & (ATTR_HIDDEN | ATTR_SYSTEM)); + } else { + attr = (attribute & 0x00005137) | ATTR_ARCHIVE; + attr &= ~(ATTR_DIRECTORY); + if (S_ISREG(stat->mode) && (server_conf.share_fake_fscaps & + FILE_SUPPORTS_SPARSE_FILES)) + attr |= ATTR_SPARSE; + + if (smb2_get_reparse_tag_special_file(stat->mode)) + attr |= ATTR_REPARSE; + } + + return attr; +} + +static void build_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt, + __le16 hash_id) +{ + pneg_ctxt->ContextType = SMB2_PREAUTH_INTEGRITY_CAPABILITIES; + pneg_ctxt->DataLength = cpu_to_le16(38); + pneg_ctxt->HashAlgorithmCount = cpu_to_le16(1); + pneg_ctxt->Reserved = cpu_to_le32(0); + pneg_ctxt->SaltLength = cpu_to_le16(SMB311_SALT_SIZE); + get_random_bytes(pneg_ctxt->Salt, SMB311_SALT_SIZE); + pneg_ctxt->HashAlgorithms = hash_id; +} + +static void build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt, + __le16 cipher_type) +{ + pneg_ctxt->ContextType = SMB2_ENCRYPTION_CAPABILITIES; + pneg_ctxt->DataLength = cpu_to_le16(4); + pneg_ctxt->Reserved = cpu_to_le32(0); + pneg_ctxt->CipherCount = cpu_to_le16(1); + pneg_ctxt->Ciphers[0] = cipher_type; +} + +static void build_compression_ctxt(struct smb2_compression_ctx *pneg_ctxt, + __le16 comp_algo) +{ + pneg_ctxt->ContextType = SMB2_COMPRESSION_CAPABILITIES; + pneg_ctxt->DataLength = + cpu_to_le16(sizeof(struct smb2_compression_ctx) + - sizeof(struct smb2_neg_context)); + pneg_ctxt->Reserved = cpu_to_le32(0); + pneg_ctxt->CompressionAlgorithmCount = cpu_to_le16(1); + pneg_ctxt->Reserved1 = cpu_to_le32(0); + pneg_ctxt->CompressionAlgorithms[0] = comp_algo; +} + +static void build_sign_cap_ctxt(struct smb2_signing_capabilities *pneg_ctxt, + __le16 sign_algo) +{ + pneg_ctxt->ContextType = SMB2_SIGNING_CAPABILITIES; + pneg_ctxt->DataLength = + cpu_to_le16((sizeof(struct smb2_signing_capabilities) + 2) + - sizeof(struct smb2_neg_context)); + pneg_ctxt->Reserved = cpu_to_le32(0); + pneg_ctxt->SigningAlgorithmCount = cpu_to_le16(1); + pneg_ctxt->SigningAlgorithms[0] = sign_algo; +} + +static void build_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt) +{ + pneg_ctxt->ContextType = SMB2_POSIX_EXTENSIONS_AVAILABLE; + pneg_ctxt->DataLength = cpu_to_le16(POSIX_CTXT_DATA_LEN); + /* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */ + pneg_ctxt->Name[0] = 0x93; + pneg_ctxt->Name[1] = 0xAD; + pneg_ctxt->Name[2] = 0x25; + pneg_ctxt->Name[3] = 0x50; + pneg_ctxt->Name[4] = 0x9C; + pneg_ctxt->Name[5] = 0xB4; + pneg_ctxt->Name[6] = 0x11; + pneg_ctxt->Name[7] = 0xE7; + pneg_ctxt->Name[8] = 0xB4; + pneg_ctxt->Name[9] = 0x23; + pneg_ctxt->Name[10] = 0x83; + pneg_ctxt->Name[11] = 0xDE; + pneg_ctxt->Name[12] = 0x96; + pneg_ctxt->Name[13] = 0x8B; + pneg_ctxt->Name[14] = 0xCD; + pneg_ctxt->Name[15] = 0x7C; +} + +static void assemble_neg_contexts(struct ksmbd_conn *conn, + struct smb2_negotiate_rsp *rsp, + void *smb2_buf_len) +{ + char *pneg_ctxt = (char *)rsp + + le32_to_cpu(rsp->NegotiateContextOffset); + int neg_ctxt_cnt = 1; + int ctxt_size; + + ksmbd_debug(SMB, + "assemble SMB2_PREAUTH_INTEGRITY_CAPABILITIES context\n"); + build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt, + conn->preauth_info->Preauth_HashId); + rsp->NegotiateContextCount = cpu_to_le16(neg_ctxt_cnt); + inc_rfc1001_len(smb2_buf_len, AUTH_GSS_PADDING); + ctxt_size = sizeof(struct smb2_preauth_neg_context); + /* Round to 8 byte boundary */ + pneg_ctxt += round_up(sizeof(struct smb2_preauth_neg_context), 8); + + if (conn->cipher_type) { + ctxt_size = round_up(ctxt_size, 8); + ksmbd_debug(SMB, + "assemble SMB2_ENCRYPTION_CAPABILITIES context\n"); + build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt, + conn->cipher_type); + rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); + ctxt_size += sizeof(struct smb2_encryption_neg_context) + 2; + /* Round to 8 byte boundary */ + pneg_ctxt += + round_up(sizeof(struct smb2_encryption_neg_context) + 2, + 8); + } + + if (conn->compress_algorithm) { + ctxt_size = round_up(ctxt_size, 8); + ksmbd_debug(SMB, + "assemble SMB2_COMPRESSION_CAPABILITIES context\n"); + /* Temporarily set to SMB3_COMPRESS_NONE */ + build_compression_ctxt((struct smb2_compression_ctx *)pneg_ctxt, + conn->compress_algorithm); + rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); + ctxt_size += sizeof(struct smb2_compression_ctx) + 2; + /* Round to 8 byte boundary */ + pneg_ctxt += round_up(sizeof(struct smb2_compression_ctx) + 2, + 8); + } + + if (conn->posix_ext_supported) { + ctxt_size = round_up(ctxt_size, 8); + ksmbd_debug(SMB, + "assemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n"); + build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt); + rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); + ctxt_size += sizeof(struct smb2_posix_neg_context); + /* Round to 8 byte boundary */ + pneg_ctxt += round_up(sizeof(struct smb2_posix_neg_context), 8); + } + + if (conn->signing_negotiated) { + ctxt_size = round_up(ctxt_size, 8); + ksmbd_debug(SMB, + "assemble SMB2_SIGNING_CAPABILITIES context\n"); + build_sign_cap_ctxt((struct smb2_signing_capabilities *)pneg_ctxt, + conn->signing_algorithm); + rsp->NegotiateContextCount = cpu_to_le16(++neg_ctxt_cnt); + ctxt_size += sizeof(struct smb2_signing_capabilities) + 2; + } + + inc_rfc1001_len(smb2_buf_len, ctxt_size); +} + +static __le32 decode_preauth_ctxt(struct ksmbd_conn *conn, + struct smb2_preauth_neg_context *pneg_ctxt) +{ + __le32 err = STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP; + + if (pneg_ctxt->HashAlgorithms == SMB2_PREAUTH_INTEGRITY_SHA512) { + conn->preauth_info->Preauth_HashId = + SMB2_PREAUTH_INTEGRITY_SHA512; + err = STATUS_SUCCESS; + } + + return err; +} + +static void decode_encrypt_ctxt(struct ksmbd_conn *conn, + struct smb2_encryption_neg_context *pneg_ctxt, + int len_of_ctxts) +{ + int cph_cnt = le16_to_cpu(pneg_ctxt->CipherCount); + int i, cphs_size = cph_cnt * sizeof(__le16); + + conn->cipher_type = 0; + + if (sizeof(struct smb2_encryption_neg_context) + cphs_size > + len_of_ctxts) { + pr_err("Invalid cipher count(%d)\n", cph_cnt); + return; + } + + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) + return; + + for (i = 0; i < cph_cnt; i++) { + if (pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES128_GCM || + pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES128_CCM || + pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES256_CCM || + pneg_ctxt->Ciphers[i] == SMB2_ENCRYPTION_AES256_GCM) { + ksmbd_debug(SMB, "Cipher ID = 0x%x\n", + pneg_ctxt->Ciphers[i]); + conn->cipher_type = pneg_ctxt->Ciphers[i]; + break; + } + } +} + +/** + * smb3_encryption_negotiated() - checks if server and client agreed on enabling encryption + * @conn: smb connection + * + * Return: true if connection should be encrypted, else false + */ +bool smb3_encryption_negotiated(struct ksmbd_conn *conn) +{ + if (!conn->ops->generate_encryptionkey) + return false; + + /* + * SMB 3.0 and 3.0.2 dialects use the SMB2_GLOBAL_CAP_ENCRYPTION flag. + * SMB 3.1.1 uses the cipher_type field. + */ + return (conn->vals->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION) || + conn->cipher_type; +} + +static void decode_compress_ctxt(struct ksmbd_conn *conn, + struct smb2_compression_ctx *pneg_ctxt) +{ + conn->compress_algorithm = SMB3_COMPRESS_NONE; +} + +static void decode_sign_cap_ctxt(struct ksmbd_conn *conn, + struct smb2_signing_capabilities *pneg_ctxt, + int len_of_ctxts) +{ + int sign_algo_cnt = le16_to_cpu(pneg_ctxt->SigningAlgorithmCount); + int i, sign_alos_size = sign_algo_cnt * sizeof(__le16); + + conn->signing_negotiated = false; + + if (sizeof(struct smb2_signing_capabilities) + sign_alos_size > + len_of_ctxts) { + pr_err("Invalid signing algorithm count(%d)\n", sign_algo_cnt); + return; + } + + for (i = 0; i < sign_algo_cnt; i++) { + if (pneg_ctxt->SigningAlgorithms[i] == SIGNING_ALG_HMAC_SHA256 || + pneg_ctxt->SigningAlgorithms[i] == SIGNING_ALG_AES_CMAC) { + ksmbd_debug(SMB, "Signing Algorithm ID = 0x%x\n", + pneg_ctxt->SigningAlgorithms[i]); + conn->signing_negotiated = true; + conn->signing_algorithm = + pneg_ctxt->SigningAlgorithms[i]; + break; + } + } +} + +static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, + struct smb2_negotiate_req *req, + int len_of_smb) +{ + /* +4 is to account for the RFC1001 len field */ + struct smb2_neg_context *pctx = (struct smb2_neg_context *)req; + int i = 0, len_of_ctxts; + int offset = le32_to_cpu(req->NegotiateContextOffset); + int neg_ctxt_cnt = le16_to_cpu(req->NegotiateContextCount); + __le32 status = STATUS_INVALID_PARAMETER; + + ksmbd_debug(SMB, "decoding %d negotiate contexts\n", neg_ctxt_cnt); + if (len_of_smb <= offset) { + ksmbd_debug(SMB, "Invalid response: negotiate context offset\n"); + return status; + } + + len_of_ctxts = len_of_smb - offset; + + while (i++ < neg_ctxt_cnt) { + int clen; + + /* check that offset is not beyond end of SMB */ + if (len_of_ctxts == 0) + break; + + if (len_of_ctxts < sizeof(struct smb2_neg_context)) + break; + + pctx = (struct smb2_neg_context *)((char *)pctx + offset); + clen = le16_to_cpu(pctx->DataLength); + if (clen + sizeof(struct smb2_neg_context) > len_of_ctxts) + break; + + if (pctx->ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES) { + ksmbd_debug(SMB, + "deassemble SMB2_PREAUTH_INTEGRITY_CAPABILITIES context\n"); + if (conn->preauth_info->Preauth_HashId) + break; + + status = decode_preauth_ctxt(conn, + (struct smb2_preauth_neg_context *)pctx); + if (status != STATUS_SUCCESS) + break; + } else if (pctx->ContextType == SMB2_ENCRYPTION_CAPABILITIES) { + ksmbd_debug(SMB, + "deassemble SMB2_ENCRYPTION_CAPABILITIES context\n"); + if (conn->cipher_type) + break; + + decode_encrypt_ctxt(conn, + (struct smb2_encryption_neg_context *)pctx, + len_of_ctxts); + } else if (pctx->ContextType == SMB2_COMPRESSION_CAPABILITIES) { + ksmbd_debug(SMB, + "deassemble SMB2_COMPRESSION_CAPABILITIES context\n"); + if (conn->compress_algorithm) + break; + + decode_compress_ctxt(conn, + (struct smb2_compression_ctx *)pctx); + } else if (pctx->ContextType == SMB2_NETNAME_NEGOTIATE_CONTEXT_ID) { + ksmbd_debug(SMB, + "deassemble SMB2_NETNAME_NEGOTIATE_CONTEXT_ID context\n"); + } else if (pctx->ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE) { + ksmbd_debug(SMB, + "deassemble SMB2_POSIX_EXTENSIONS_AVAILABLE context\n"); + conn->posix_ext_supported = true; + } else if (pctx->ContextType == SMB2_SIGNING_CAPABILITIES) { + ksmbd_debug(SMB, + "deassemble SMB2_SIGNING_CAPABILITIES context\n"); + decode_sign_cap_ctxt(conn, + (struct smb2_signing_capabilities *)pctx, + len_of_ctxts); + } + + /* offsets must be 8 byte aligned */ + clen = (clen + 7) & ~0x7; + offset = clen + sizeof(struct smb2_neg_context); + len_of_ctxts -= clen + sizeof(struct smb2_neg_context); + } + return status; +} + +/** + * smb2_handle_negotiate() - handler for smb2 negotiate command + * @work: smb work containing smb request buffer + * + * Return: 0 + */ +int smb2_handle_negotiate(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_negotiate_req *req = smb2_get_msg(work->request_buf); + struct smb2_negotiate_rsp *rsp = smb2_get_msg(work->response_buf); + int rc = 0; + unsigned int smb2_buf_len, smb2_neg_size; + __le32 status; + + ksmbd_debug(SMB, "Received negotiate request\n"); + conn->need_neg = false; + if (ksmbd_conn_good(work)) { + pr_err("conn->tcp_status is already in CifsGood State\n"); + work->send_no_response = 1; + return rc; + } + + if (req->DialectCount == 0) { + pr_err("malformed packet\n"); + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + + smb2_buf_len = get_rfc1002_len(work->request_buf); + smb2_neg_size = offsetof(struct smb2_negotiate_req, Dialects); + if (smb2_neg_size > smb2_buf_len) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + + if (conn->dialect == SMB311_PROT_ID) { + unsigned int nego_ctxt_off = le32_to_cpu(req->NegotiateContextOffset); + + if (smb2_buf_len < nego_ctxt_off) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + + if (smb2_neg_size > nego_ctxt_off) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + + if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) > + nego_ctxt_off) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + } else { + if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) > + smb2_buf_len) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + rc = -EINVAL; + goto err_out; + } + } + + conn->cli_cap = le32_to_cpu(req->Capabilities); + switch (conn->dialect) { + case SMB311_PROT_ID: + conn->preauth_info = + kzalloc(sizeof(struct preauth_integrity_info), + GFP_KERNEL); + if (!conn->preauth_info) { + rc = -ENOMEM; + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + goto err_out; + } + + status = deassemble_neg_contexts(conn, req, + get_rfc1002_len(work->request_buf)); + if (status != STATUS_SUCCESS) { + pr_err("deassemble_neg_contexts error(0x%x)\n", + status); + rsp->hdr.Status = status; + rc = -EINVAL; + kfree(conn->preauth_info); + conn->preauth_info = NULL; + goto err_out; + } + + rc = init_smb3_11_server(conn); + if (rc < 0) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + kfree(conn->preauth_info); + conn->preauth_info = NULL; + goto err_out; + } + + ksmbd_gen_preauth_integrity_hash(conn, + work->request_buf, + conn->preauth_info->Preauth_HashValue); + rsp->NegotiateContextOffset = + cpu_to_le32(OFFSET_OF_NEG_CONTEXT); + assemble_neg_contexts(conn, rsp, work->response_buf); + break; + case SMB302_PROT_ID: + init_smb3_02_server(conn); + break; + case SMB30_PROT_ID: + init_smb3_0_server(conn); + break; + case SMB21_PROT_ID: + init_smb2_1_server(conn); + break; + case SMB20_PROT_ID: + rc = init_smb2_0_server(conn); + if (rc) { + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + goto err_out; + } + break; + case SMB2X_PROT_ID: + case BAD_PROT_ID: + default: + ksmbd_debug(SMB, "Server dialect :0x%x not supported\n", + conn->dialect); + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + rc = -EINVAL; + goto err_out; + } + rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); + + /* For stats */ + conn->connection_type = conn->dialect; + + rsp->MaxTransactSize = cpu_to_le32(conn->vals->max_trans_size); + rsp->MaxReadSize = cpu_to_le32(conn->vals->max_read_size); + rsp->MaxWriteSize = cpu_to_le32(conn->vals->max_write_size); + + if (conn->dialect > SMB20_PROT_ID) { + memcpy(conn->ClientGUID, req->ClientGUID, + SMB2_CLIENT_GUID_SIZE); + conn->cli_sec_mode = le16_to_cpu(req->SecurityMode); + } + + rsp->StructureSize = cpu_to_le16(65); + rsp->DialectRevision = cpu_to_le16(conn->dialect); + /* Not setting conn guid rsp->ServerGUID, as it + * not used by client for identifying server + */ + memset(rsp->ServerGUID, 0, SMB2_CLIENT_GUID_SIZE); + + rsp->SystemTime = cpu_to_le64(ksmbd_systime()); + rsp->ServerStartTime = 0; + ksmbd_debug(SMB, "negotiate context offset %d, count %d\n", + le32_to_cpu(rsp->NegotiateContextOffset), + le16_to_cpu(rsp->NegotiateContextCount)); + + rsp->SecurityBufferOffset = cpu_to_le16(128); + rsp->SecurityBufferLength = cpu_to_le16(AUTH_GSS_LENGTH); + ksmbd_copy_gss_neg_header((char *)(&rsp->hdr) + + le16_to_cpu(rsp->SecurityBufferOffset)); + inc_rfc1001_len(work->response_buf, sizeof(struct smb2_negotiate_rsp) - + sizeof(struct smb2_hdr) - sizeof(rsp->Buffer) + + AUTH_GSS_LENGTH); + rsp->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED_LE; + conn->use_spnego = true; + + if ((server_conf.signing == KSMBD_CONFIG_OPT_AUTO || + server_conf.signing == KSMBD_CONFIG_OPT_DISABLED) && + req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED_LE) + conn->sign = true; + else if (server_conf.signing == KSMBD_CONFIG_OPT_MANDATORY) { + server_conf.enforced_signing = true; + rsp->SecurityMode |= SMB2_NEGOTIATE_SIGNING_REQUIRED_LE; + conn->sign = true; + } + + conn->srv_sec_mode = le16_to_cpu(rsp->SecurityMode); + ksmbd_conn_set_need_negotiate(work); + +err_out: + if (rc < 0) + smb2_set_err_rsp(work); + + return rc; +} + +static int alloc_preauth_hash(struct ksmbd_session *sess, + struct ksmbd_conn *conn) +{ + if (sess->Preauth_HashValue) + return 0; + + sess->Preauth_HashValue = kmemdup(conn->preauth_info->Preauth_HashValue, + PREAUTH_HASHVALUE_SIZE, GFP_KERNEL); + if (!sess->Preauth_HashValue) + return -ENOMEM; + + return 0; +} + +static int generate_preauth_hash(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + u8 *preauth_hash; + + if (conn->dialect != SMB311_PROT_ID) + return 0; + + if (conn->binding) { + struct preauth_session *preauth_sess; + + preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id); + if (!preauth_sess) { + preauth_sess = ksmbd_preauth_session_alloc(conn, sess->id); + if (!preauth_sess) + return -ENOMEM; + } + + preauth_hash = preauth_sess->Preauth_HashValue; + } else { + if (!sess->Preauth_HashValue) + if (alloc_preauth_hash(sess, conn)) + return -ENOMEM; + preauth_hash = sess->Preauth_HashValue; + } + + ksmbd_gen_preauth_integrity_hash(conn, work->request_buf, preauth_hash); + return 0; +} + +static int decode_negotiation_token(struct ksmbd_conn *conn, + struct negotiate_message *negblob, + size_t sz) +{ + if (!conn->use_spnego) + return -EINVAL; + + if (ksmbd_decode_negTokenInit((char *)negblob, sz, conn)) { + if (ksmbd_decode_negTokenTarg((char *)negblob, sz, conn)) { + conn->auth_mechs |= KSMBD_AUTH_NTLMSSP; + conn->preferred_auth_mech = KSMBD_AUTH_NTLMSSP; + conn->use_spnego = false; + } + } + return 0; +} + +static int ntlm_negotiate(struct ksmbd_work *work, + struct negotiate_message *negblob, + size_t negblob_len) +{ + struct smb2_sess_setup_rsp *rsp = smb2_get_msg(work->response_buf); + struct challenge_message *chgblob; + unsigned char *spnego_blob = NULL; + u16 spnego_blob_len; + char *neg_blob; + int sz, rc; + + ksmbd_debug(SMB, "negotiate phase\n"); + rc = ksmbd_decode_ntlmssp_neg_blob(negblob, negblob_len, work->conn); + if (rc) + return rc; + + sz = le16_to_cpu(rsp->SecurityBufferOffset); + chgblob = + (struct challenge_message *)((char *)&rsp->hdr.ProtocolId + sz); + memset(chgblob, 0, sizeof(struct challenge_message)); + + if (!work->conn->use_spnego) { + sz = ksmbd_build_ntlmssp_challenge_blob(chgblob, work->conn); + if (sz < 0) + return -ENOMEM; + + rsp->SecurityBufferLength = cpu_to_le16(sz); + return 0; + } + + sz = sizeof(struct challenge_message); + sz += (strlen(ksmbd_netbios_name()) * 2 + 1 + 4) * 6; + + neg_blob = kzalloc(sz, GFP_KERNEL); + if (!neg_blob) + return -ENOMEM; + + chgblob = (struct challenge_message *)neg_blob; + sz = ksmbd_build_ntlmssp_challenge_blob(chgblob, work->conn); + if (sz < 0) { + rc = -ENOMEM; + goto out; + } + + rc = build_spnego_ntlmssp_neg_blob(&spnego_blob, &spnego_blob_len, + neg_blob, sz); + if (rc) { + rc = -ENOMEM; + goto out; + } + + sz = le16_to_cpu(rsp->SecurityBufferOffset); + memcpy((char *)&rsp->hdr.ProtocolId + sz, spnego_blob, spnego_blob_len); + rsp->SecurityBufferLength = cpu_to_le16(spnego_blob_len); + +out: + kfree(spnego_blob); + kfree(neg_blob); + return rc; +} + +static struct authenticate_message *user_authblob(struct ksmbd_conn *conn, + struct smb2_sess_setup_req *req) +{ + int sz; + + if (conn->use_spnego && conn->mechToken) + return (struct authenticate_message *)conn->mechToken; + + sz = le16_to_cpu(req->SecurityBufferOffset); + return (struct authenticate_message *)((char *)&req->hdr.ProtocolId + + sz); +} + +static struct ksmbd_user *session_user(struct ksmbd_conn *conn, + struct smb2_sess_setup_req *req) +{ + struct authenticate_message *authblob; + struct ksmbd_user *user; + char *name; + unsigned int auth_msg_len, name_off, name_len, secbuf_len; + + secbuf_len = le16_to_cpu(req->SecurityBufferLength); + if (secbuf_len < sizeof(struct authenticate_message)) { + ksmbd_debug(SMB, "blob len %d too small\n", secbuf_len); + return NULL; + } + authblob = user_authblob(conn, req); + name_off = le32_to_cpu(authblob->UserName.BufferOffset); + name_len = le16_to_cpu(authblob->UserName.Length); + auth_msg_len = le16_to_cpu(req->SecurityBufferOffset) + secbuf_len; + + if (auth_msg_len < (u64)name_off + name_len) + return NULL; + + name = smb_strndup_from_utf16((const char *)authblob + name_off, + name_len, + true, + conn->local_nls); + if (IS_ERR(name)) { + pr_err("cannot allocate memory\n"); + return NULL; + } + + ksmbd_debug(SMB, "session setup request for user %s\n", name); + user = ksmbd_login_user(name); + kfree(name); + return user; +} + +static int ntlm_authenticate(struct ksmbd_work *work) +{ + struct smb2_sess_setup_req *req = smb2_get_msg(work->request_buf); + struct smb2_sess_setup_rsp *rsp = smb2_get_msg(work->response_buf); + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + struct channel *chann = NULL; + struct ksmbd_user *user; + u64 prev_id; + int sz, rc; + + ksmbd_debug(SMB, "authenticate phase\n"); + if (conn->use_spnego) { + unsigned char *spnego_blob; + u16 spnego_blob_len; + + rc = build_spnego_ntlmssp_auth_blob(&spnego_blob, + &spnego_blob_len, + 0); + if (rc) + return -ENOMEM; + + sz = le16_to_cpu(rsp->SecurityBufferOffset); + memcpy((char *)&rsp->hdr.ProtocolId + sz, spnego_blob, spnego_blob_len); + rsp->SecurityBufferLength = cpu_to_le16(spnego_blob_len); + kfree(spnego_blob); + inc_rfc1001_len(work->response_buf, spnego_blob_len - 1); + } + + user = session_user(conn, req); + if (!user) { + ksmbd_debug(SMB, "Unknown user name or an error\n"); + return -EPERM; + } + + /* Check for previous session */ + prev_id = le64_to_cpu(req->PreviousSessionId); + if (prev_id && prev_id != sess->id) + destroy_previous_session(conn, user, prev_id); + + if (sess->state == SMB2_SESSION_VALID) { + /* + * Reuse session if anonymous try to connect + * on reauthetication. + */ + if (ksmbd_anonymous_user(user)) { + ksmbd_free_user(user); + return 0; + } + + if (!ksmbd_compare_user(sess->user, user)) { + ksmbd_free_user(user); + return -EPERM; + } + ksmbd_free_user(user); + } else { + sess->user = user; + } + + if (user_guest(sess->user)) { + rsp->SessionFlags = SMB2_SESSION_FLAG_IS_GUEST_LE; + } else { + struct authenticate_message *authblob; + + authblob = user_authblob(conn, req); + sz = le16_to_cpu(req->SecurityBufferLength); + rc = ksmbd_decode_ntlmssp_auth_blob(authblob, sz, conn, sess); + if (rc) { + set_user_flag(sess->user, KSMBD_USER_FLAG_BAD_PASSWORD); + ksmbd_debug(SMB, "authentication failed\n"); + return -EPERM; + } + } + + /* + * If session state is SMB2_SESSION_VALID, We can assume + * that it is reauthentication. And the user/password + * has been verified, so return it here. + */ + if (sess->state == SMB2_SESSION_VALID) { + if (conn->binding) + goto binding_session; + return 0; + } + + if ((rsp->SessionFlags != SMB2_SESSION_FLAG_IS_GUEST_LE && + (conn->sign || server_conf.enforced_signing)) || + (req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) + sess->sign = true; + + if (smb3_encryption_negotiated(conn) && + !(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { + rc = conn->ops->generate_encryptionkey(conn, sess); + if (rc) { + ksmbd_debug(SMB, + "SMB3 encryption key generation failed\n"); + return -EINVAL; + } + sess->enc = true; + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION) + rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; + /* + * signing is disable if encryption is enable + * on this session + */ + sess->sign = false; + } + +binding_session: + if (conn->dialect >= SMB30_PROT_ID) { + chann = lookup_chann_list(sess, conn); + if (!chann) { + chann = kmalloc(sizeof(struct channel), GFP_KERNEL); + if (!chann) + return -ENOMEM; + + chann->conn = conn; + xa_store(&sess->ksmbd_chann_list, (long)conn, chann, GFP_KERNEL); + } + } + + if (conn->ops->generate_signingkey) { + rc = conn->ops->generate_signingkey(sess, conn); + if (rc) { + ksmbd_debug(SMB, "SMB3 signing key generation failed\n"); + return -EINVAL; + } + } + + if (conn->dialect > SMB20_PROT_ID) { + if (!ksmbd_conn_lookup_dialect(conn)) { + pr_err("fail to verify the dialect\n"); + return -ENOENT; + } + } + return 0; +} + +#ifdef CONFIG_SMB_SERVER_KERBEROS5 +static int krb5_authenticate(struct ksmbd_work *work) +{ + struct smb2_sess_setup_req *req = smb2_get_msg(work->request_buf); + struct smb2_sess_setup_rsp *rsp = smb2_get_msg(work->response_buf); + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + char *in_blob, *out_blob; + struct channel *chann = NULL; + u64 prev_sess_id; + int in_len, out_len; + int retval; + + in_blob = (char *)&req->hdr.ProtocolId + + le16_to_cpu(req->SecurityBufferOffset); + in_len = le16_to_cpu(req->SecurityBufferLength); + out_blob = (char *)&rsp->hdr.ProtocolId + + le16_to_cpu(rsp->SecurityBufferOffset); + out_len = work->response_sz - + (le16_to_cpu(rsp->SecurityBufferOffset) + 4); + + /* Check previous session */ + prev_sess_id = le64_to_cpu(req->PreviousSessionId); + if (prev_sess_id && prev_sess_id != sess->id) + destroy_previous_session(conn, sess->user, prev_sess_id); + + if (sess->state == SMB2_SESSION_VALID) + ksmbd_free_user(sess->user); + + retval = ksmbd_krb5_authenticate(sess, in_blob, in_len, + out_blob, &out_len); + if (retval) { + ksmbd_debug(SMB, "krb5 authentication failed\n"); + return -EINVAL; + } + rsp->SecurityBufferLength = cpu_to_le16(out_len); + inc_rfc1001_len(work->response_buf, out_len - 1); + + if ((conn->sign || server_conf.enforced_signing) || + (req->SecurityMode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) + sess->sign = true; + + if (smb3_encryption_negotiated(conn)) { + retval = conn->ops->generate_encryptionkey(conn, sess); + if (retval) { + ksmbd_debug(SMB, + "SMB3 encryption key generation failed\n"); + return -EINVAL; + } + sess->enc = true; + if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION) + rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE; + sess->sign = false; + } + + if (conn->dialect >= SMB30_PROT_ID) { + chann = lookup_chann_list(sess, conn); + if (!chann) { + chann = kmalloc(sizeof(struct channel), GFP_KERNEL); + if (!chann) + return -ENOMEM; + + chann->conn = conn; + xa_store(&sess->ksmbd_chann_list, (long)conn, chann, GFP_KERNEL); + } + } + + if (conn->ops->generate_signingkey) { + retval = conn->ops->generate_signingkey(sess, conn); + if (retval) { + ksmbd_debug(SMB, "SMB3 signing key generation failed\n"); + return -EINVAL; + } + } + + if (conn->dialect > SMB20_PROT_ID) { + if (!ksmbd_conn_lookup_dialect(conn)) { + pr_err("fail to verify the dialect\n"); + return -ENOENT; + } + } + return 0; +} +#else +static int krb5_authenticate(struct ksmbd_work *work) +{ + return -EOPNOTSUPP; +} +#endif + +int smb2_sess_setup(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_sess_setup_req *req = smb2_get_msg(work->request_buf); + struct smb2_sess_setup_rsp *rsp = smb2_get_msg(work->response_buf); + struct ksmbd_session *sess; + struct negotiate_message *negblob; + unsigned int negblob_len, negblob_off; + int rc = 0; + + ksmbd_debug(SMB, "Received request for session setup\n"); + + rsp->StructureSize = cpu_to_le16(9); + rsp->SessionFlags = 0; + rsp->SecurityBufferOffset = cpu_to_le16(72); + rsp->SecurityBufferLength = 0; + inc_rfc1001_len(work->response_buf, 9); + + if (!req->hdr.SessionId) { + sess = ksmbd_smb2_session_create(); + if (!sess) { + rc = -ENOMEM; + goto out_err; + } + rsp->hdr.SessionId = cpu_to_le64(sess->id); + rc = ksmbd_session_register(conn, sess); + if (rc) + goto out_err; + } else if (conn->dialect >= SMB30_PROT_ID && + (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) && + req->Flags & SMB2_SESSION_REQ_FLAG_BINDING) { + u64 sess_id = le64_to_cpu(req->hdr.SessionId); + + sess = ksmbd_session_lookup_slowpath(sess_id); + if (!sess) { + rc = -ENOENT; + goto out_err; + } + + if (conn->dialect != sess->dialect) { + rc = -EINVAL; + goto out_err; + } + + if (!(req->hdr.Flags & SMB2_FLAGS_SIGNED)) { + rc = -EINVAL; + goto out_err; + } + + if (strncmp(conn->ClientGUID, sess->ClientGUID, + SMB2_CLIENT_GUID_SIZE)) { + rc = -ENOENT; + goto out_err; + } + + if (sess->state == SMB2_SESSION_IN_PROGRESS) { + rc = -EACCES; + goto out_err; + } + + if (sess->state == SMB2_SESSION_EXPIRED) { + rc = -EFAULT; + goto out_err; + } + + if (ksmbd_session_lookup(conn, sess_id)) { + rc = -EACCES; + goto out_err; + } + + conn->binding = true; + } else if ((conn->dialect < SMB30_PROT_ID || + server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) && + (req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { + sess = NULL; + rc = -EACCES; + goto out_err; + } else { + sess = ksmbd_session_lookup(conn, + le64_to_cpu(req->hdr.SessionId)); + if (!sess) { + rc = -ENOENT; + goto out_err; + } + } + work->sess = sess; + + if (sess->state == SMB2_SESSION_EXPIRED) + sess->state = SMB2_SESSION_IN_PROGRESS; + + negblob_off = le16_to_cpu(req->SecurityBufferOffset); + negblob_len = le16_to_cpu(req->SecurityBufferLength); + if (negblob_off < offsetof(struct smb2_sess_setup_req, Buffer) || + negblob_len < offsetof(struct negotiate_message, NegotiateFlags)) { + rc = -EINVAL; + goto out_err; + } + + negblob = (struct negotiate_message *)((char *)&req->hdr.ProtocolId + + negblob_off); + + if (decode_negotiation_token(conn, negblob, negblob_len) == 0) { + if (conn->mechToken) + negblob = (struct negotiate_message *)conn->mechToken; + } + + if (server_conf.auth_mechs & conn->auth_mechs) { + rc = generate_preauth_hash(work); + if (rc) + goto out_err; + + if (conn->preferred_auth_mech & + (KSMBD_AUTH_KRB5 | KSMBD_AUTH_MSKRB5)) { + rc = krb5_authenticate(work); + if (rc) { + rc = -EINVAL; + goto out_err; + } + + ksmbd_conn_set_good(work); + sess->state = SMB2_SESSION_VALID; + kfree(sess->Preauth_HashValue); + sess->Preauth_HashValue = NULL; + } else if (conn->preferred_auth_mech == KSMBD_AUTH_NTLMSSP) { + if (negblob->MessageType == NtLmNegotiate) { + rc = ntlm_negotiate(work, negblob, negblob_len); + if (rc) + goto out_err; + rsp->hdr.Status = + STATUS_MORE_PROCESSING_REQUIRED; + /* + * Note: here total size -1 is done as an + * adjustment for 0 size blob + */ + inc_rfc1001_len(work->response_buf, + le16_to_cpu(rsp->SecurityBufferLength) - 1); + + } else if (negblob->MessageType == NtLmAuthenticate) { + rc = ntlm_authenticate(work); + if (rc) + goto out_err; + + ksmbd_conn_set_good(work); + sess->state = SMB2_SESSION_VALID; + if (conn->binding) { + struct preauth_session *preauth_sess; + + preauth_sess = + ksmbd_preauth_session_lookup(conn, sess->id); + if (preauth_sess) { + list_del(&preauth_sess->preauth_entry); + kfree(preauth_sess); + } + } + kfree(sess->Preauth_HashValue); + sess->Preauth_HashValue = NULL; + } + } else { + /* TODO: need one more negotiation */ + pr_err("Not support the preferred authentication\n"); + rc = -EINVAL; + } + } else { + pr_err("Not support authentication\n"); + rc = -EINVAL; + } + +out_err: + if (rc == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (rc == -ENOENT) + rsp->hdr.Status = STATUS_USER_SESSION_DELETED; + else if (rc == -EACCES) + rsp->hdr.Status = STATUS_REQUEST_NOT_ACCEPTED; + else if (rc == -EFAULT) + rsp->hdr.Status = STATUS_NETWORK_SESSION_EXPIRED; + else if (rc == -ENOMEM) + rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; + else if (rc) + rsp->hdr.Status = STATUS_LOGON_FAILURE; + + if (conn->use_spnego && conn->mechToken) { + kfree(conn->mechToken); + conn->mechToken = NULL; + } + + if (rc < 0) { + /* + * SecurityBufferOffset should be set to zero + * in session setup error response. + */ + rsp->SecurityBufferOffset = 0; + + if (sess) { + bool try_delay = false; + + /* + * To avoid dictionary attacks (repeated session setups rapidly sent) to + * connect to server, ksmbd make a delay of a 5 seconds on session setup + * failure to make it harder to send enough random connection requests + * to break into a server. + */ + if (sess->user && sess->user->flags & KSMBD_USER_FLAG_DELAY_SESSION) + try_delay = true; + + xa_erase(&conn->sessions, sess->id); + ksmbd_session_destroy(sess); + work->sess = NULL; + if (try_delay) + ssleep(5); + } + } + + return rc; +} + +/** + * smb2_tree_connect() - handler for smb2 tree connect command + * @work: smb work containing smb request buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_tree_connect(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_tree_connect_req *req = smb2_get_msg(work->request_buf); + struct smb2_tree_connect_rsp *rsp = smb2_get_msg(work->response_buf); + struct ksmbd_session *sess = work->sess; + char *treename = NULL, *name = NULL; + struct ksmbd_tree_conn_status status; + struct ksmbd_share_config *share; + int rc = -EINVAL; + + treename = smb_strndup_from_utf16(req->Buffer, + le16_to_cpu(req->PathLength), true, + conn->local_nls); + if (IS_ERR(treename)) { + pr_err("treename is NULL\n"); + status.ret = KSMBD_TREE_CONN_STATUS_ERROR; + goto out_err1; + } + + name = ksmbd_extract_sharename(conn->um, treename); + if (IS_ERR(name)) { + status.ret = KSMBD_TREE_CONN_STATUS_ERROR; + goto out_err1; + } + + ksmbd_debug(SMB, "tree connect request for tree %s treename %s\n", + name, treename); + + status = ksmbd_tree_conn_connect(conn, sess, name); + if (status.ret == KSMBD_TREE_CONN_STATUS_OK) + rsp->hdr.Id.SyncId.TreeId = cpu_to_le32(status.tree_conn->id); + else + goto out_err1; + + share = status.tree_conn->share_conf; + if (test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "IPC share path request\n"); + rsp->ShareType = SMB2_SHARE_TYPE_PIPE; + rsp->MaximalAccess = FILE_READ_DATA_LE | FILE_READ_EA_LE | + FILE_EXECUTE_LE | FILE_READ_ATTRIBUTES_LE | + FILE_DELETE_LE | FILE_READ_CONTROL_LE | + FILE_WRITE_DAC_LE | FILE_WRITE_OWNER_LE | + FILE_SYNCHRONIZE_LE; + } else { + rsp->ShareType = SMB2_SHARE_TYPE_DISK; + rsp->MaximalAccess = FILE_READ_DATA_LE | FILE_READ_EA_LE | + FILE_EXECUTE_LE | FILE_READ_ATTRIBUTES_LE; + if (test_tree_conn_flag(status.tree_conn, + KSMBD_TREE_CONN_FLAG_WRITABLE)) { + rsp->MaximalAccess |= FILE_WRITE_DATA_LE | + FILE_APPEND_DATA_LE | FILE_WRITE_EA_LE | + FILE_DELETE_LE | FILE_WRITE_ATTRIBUTES_LE | + FILE_DELETE_CHILD_LE | FILE_READ_CONTROL_LE | + FILE_WRITE_DAC_LE | FILE_WRITE_OWNER_LE | + FILE_SYNCHRONIZE_LE; + } + } + + status.tree_conn->maximal_access = le32_to_cpu(rsp->MaximalAccess); + if (conn->posix_ext_supported) + status.tree_conn->posix_extensions = true; + + rsp->StructureSize = cpu_to_le16(16); + inc_rfc1001_len(work->response_buf, 16); +out_err1: + rsp->Capabilities = 0; + rsp->Reserved = 0; + /* default manual caching */ + rsp->ShareFlags = SMB2_SHAREFLAG_MANUAL_CACHING; + + if (!IS_ERR(treename)) + kfree(treename); + if (!IS_ERR(name)) + kfree(name); + + switch (status.ret) { + case KSMBD_TREE_CONN_STATUS_OK: + rsp->hdr.Status = STATUS_SUCCESS; + rc = 0; + break; + case -ESTALE: + case -ENOENT: + case KSMBD_TREE_CONN_STATUS_NO_SHARE: + rsp->hdr.Status = STATUS_BAD_NETWORK_NAME; + break; + case -ENOMEM: + case KSMBD_TREE_CONN_STATUS_NOMEM: + rsp->hdr.Status = STATUS_NO_MEMORY; + break; + case KSMBD_TREE_CONN_STATUS_ERROR: + case KSMBD_TREE_CONN_STATUS_TOO_MANY_CONNS: + case KSMBD_TREE_CONN_STATUS_TOO_MANY_SESSIONS: + rsp->hdr.Status = STATUS_ACCESS_DENIED; + break; + case -EINVAL: + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + break; + default: + rsp->hdr.Status = STATUS_ACCESS_DENIED; + } + + if (status.ret != KSMBD_TREE_CONN_STATUS_OK) + smb2_set_err_rsp(work); + + return rc; +} + +/** + * smb2_create_open_flags() - convert smb open flags to unix open flags + * @file_present: is file already present + * @access: file access flags + * @disposition: file disposition flags + * @may_flags: set with MAY_ flags + * + * Return: file open flags + */ +static int smb2_create_open_flags(bool file_present, __le32 access, + __le32 disposition, + int *may_flags) +{ + int oflags = O_NONBLOCK | O_LARGEFILE; + + if (access & FILE_READ_DESIRED_ACCESS_LE && + access & FILE_WRITE_DESIRE_ACCESS_LE) { + oflags |= O_RDWR; + *may_flags = MAY_OPEN | MAY_READ | MAY_WRITE; + } else if (access & FILE_WRITE_DESIRE_ACCESS_LE) { + oflags |= O_WRONLY; + *may_flags = MAY_OPEN | MAY_WRITE; + } else { + oflags |= O_RDONLY; + *may_flags = MAY_OPEN | MAY_READ; + } + + if (access == FILE_READ_ATTRIBUTES_LE) + oflags |= O_PATH; + + if (file_present) { + switch (disposition & FILE_CREATE_MASK_LE) { + case FILE_OPEN_LE: + case FILE_CREATE_LE: + break; + case FILE_SUPERSEDE_LE: + case FILE_OVERWRITE_LE: + case FILE_OVERWRITE_IF_LE: + oflags |= O_TRUNC; + break; + default: + break; + } + } else { + switch (disposition & FILE_CREATE_MASK_LE) { + case FILE_SUPERSEDE_LE: + case FILE_CREATE_LE: + case FILE_OPEN_IF_LE: + case FILE_OVERWRITE_IF_LE: + oflags |= O_CREAT; + break; + case FILE_OPEN_LE: + case FILE_OVERWRITE_LE: + oflags &= ~O_CREAT; + break; + default: + break; + } + } + + return oflags; +} + +/** + * smb2_tree_disconnect() - handler for smb tree connect request + * @work: smb work containing request buffer + * + * Return: 0 + */ +int smb2_tree_disconnect(struct ksmbd_work *work) +{ + struct smb2_tree_disconnect_rsp *rsp = smb2_get_msg(work->response_buf); + struct ksmbd_session *sess = work->sess; + struct ksmbd_tree_connect *tcon = work->tcon; + + rsp->StructureSize = cpu_to_le16(4); + inc_rfc1001_len(work->response_buf, 4); + + ksmbd_debug(SMB, "request\n"); + + if (!tcon) { + struct smb2_tree_disconnect_req *req = + smb2_get_msg(work->request_buf); + + ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); + rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; + smb2_set_err_rsp(work); + return 0; + } + + ksmbd_close_tree_conn_fds(work); + ksmbd_tree_conn_disconnect(sess, tcon); + work->tcon = NULL; + return 0; +} + +/** + * smb2_session_logoff() - handler for session log off request + * @work: smb work containing request buffer + * + * Return: 0 + */ +int smb2_session_logoff(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_logoff_rsp *rsp = smb2_get_msg(work->response_buf); + struct ksmbd_session *sess = work->sess; + + rsp->StructureSize = cpu_to_le16(4); + inc_rfc1001_len(work->response_buf, 4); + + ksmbd_debug(SMB, "request\n"); + + /* setting CifsExiting here may race with start_tcp_sess */ + ksmbd_conn_set_need_reconnect(work); + ksmbd_close_session_fds(work); + ksmbd_conn_wait_idle(conn); + + if (ksmbd_tree_conn_session_logoff(sess)) { + struct smb2_logoff_req *req = smb2_get_msg(work->request_buf); + + ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); + rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; + smb2_set_err_rsp(work); + return 0; + } + + ksmbd_destroy_file_table(&sess->file_table); + sess->state = SMB2_SESSION_EXPIRED; + + ksmbd_free_user(sess->user); + sess->user = NULL; + + /* let start_tcp_sess free connection info now */ + ksmbd_conn_set_need_negotiate(work); + return 0; +} + +/** + * create_smb2_pipe() - create IPC pipe + * @work: smb work containing request buffer + * + * Return: 0 on success, otherwise error + */ +static noinline int create_smb2_pipe(struct ksmbd_work *work) +{ + struct smb2_create_rsp *rsp = smb2_get_msg(work->response_buf); + struct smb2_create_req *req = smb2_get_msg(work->request_buf); + int id; + int err; + char *name; + + name = smb_strndup_from_utf16(req->Buffer, le16_to_cpu(req->NameLength), + 1, work->conn->local_nls); + if (IS_ERR(name)) { + rsp->hdr.Status = STATUS_NO_MEMORY; + err = PTR_ERR(name); + goto out; + } + + id = ksmbd_session_rpc_open(work->sess, name); + if (id < 0) { + pr_err("Unable to open RPC pipe: %d\n", id); + err = id; + goto out; + } + + rsp->hdr.Status = STATUS_SUCCESS; + rsp->StructureSize = cpu_to_le16(89); + rsp->OplockLevel = SMB2_OPLOCK_LEVEL_NONE; + rsp->Reserved = 0; + rsp->CreateAction = cpu_to_le32(FILE_OPENED); + + rsp->CreationTime = cpu_to_le64(0); + rsp->LastAccessTime = cpu_to_le64(0); + rsp->ChangeTime = cpu_to_le64(0); + rsp->AllocationSize = cpu_to_le64(0); + rsp->EndofFile = cpu_to_le64(0); + rsp->FileAttributes = ATTR_NORMAL_LE; + rsp->Reserved2 = 0; + rsp->VolatileFileId = id; + rsp->PersistentFileId = 0; + rsp->CreateContextsOffset = 0; + rsp->CreateContextsLength = 0; + + inc_rfc1001_len(work->response_buf, 88); /* StructureSize - 1*/ + kfree(name); + return 0; + +out: + switch (err) { + case -EINVAL: + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + break; + case -ENOSPC: + case -ENOMEM: + rsp->hdr.Status = STATUS_NO_MEMORY; + break; + } + + if (!IS_ERR(name)) + kfree(name); + + smb2_set_err_rsp(work); + return err; +} + +/** + * smb2_set_ea() - handler for setting extended attributes using set + * info command + * @eabuf: set info command buffer + * @buf_len: set info command buffer length + * @path: dentry path for get ea + * + * Return: 0 on success, otherwise error + */ +static int smb2_set_ea(struct smb2_ea_info *eabuf, unsigned int buf_len, + const struct path *path) +{ + struct user_namespace *user_ns = mnt_user_ns(path->mnt); + char *attr_name = NULL, *value; + int rc = 0; + unsigned int next = 0; + + if (buf_len < sizeof(struct smb2_ea_info) + eabuf->EaNameLength + + le16_to_cpu(eabuf->EaValueLength)) + return -EINVAL; + + attr_name = kmalloc(XATTR_NAME_MAX + 1, GFP_KERNEL); + if (!attr_name) + return -ENOMEM; + + do { + if (!eabuf->EaNameLength) + goto next; + + ksmbd_debug(SMB, + "name : <%s>, name_len : %u, value_len : %u, next : %u\n", + eabuf->name, eabuf->EaNameLength, + le16_to_cpu(eabuf->EaValueLength), + le32_to_cpu(eabuf->NextEntryOffset)); + + if (eabuf->EaNameLength > + (XATTR_NAME_MAX - XATTR_USER_PREFIX_LEN)) { + rc = -EINVAL; + break; + } + + memcpy(attr_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN); + memcpy(&attr_name[XATTR_USER_PREFIX_LEN], eabuf->name, + eabuf->EaNameLength); + attr_name[XATTR_USER_PREFIX_LEN + eabuf->EaNameLength] = '\0'; + value = (char *)&eabuf->name + eabuf->EaNameLength + 1; + + if (!eabuf->EaValueLength) { + rc = ksmbd_vfs_casexattr_len(user_ns, + path->dentry, + attr_name, + XATTR_USER_PREFIX_LEN + + eabuf->EaNameLength); + + /* delete the EA only when it exits */ + if (rc > 0) { + rc = ksmbd_vfs_remove_xattr(user_ns, + path->dentry, + attr_name); + + if (rc < 0) { + ksmbd_debug(SMB, + "remove xattr failed(%d)\n", + rc); + break; + } + } + + /* if the EA doesn't exist, just do nothing. */ + rc = 0; + } else { + rc = ksmbd_vfs_setxattr(user_ns, + path->dentry, attr_name, value, + le16_to_cpu(eabuf->EaValueLength), 0); + if (rc < 0) { + ksmbd_debug(SMB, + "ksmbd_vfs_setxattr is failed(%d)\n", + rc); + break; + } + } + +next: + next = le32_to_cpu(eabuf->NextEntryOffset); + if (next == 0 || buf_len < next) + break; + buf_len -= next; + eabuf = (struct smb2_ea_info *)((char *)eabuf + next); + if (next < (u32)eabuf->EaNameLength + le16_to_cpu(eabuf->EaValueLength)) + break; + + } while (next != 0); + + kfree(attr_name); + return rc; +} + +static noinline int smb2_set_stream_name_xattr(const struct path *path, + struct ksmbd_file *fp, + char *stream_name, int s_type) +{ + struct user_namespace *user_ns = mnt_user_ns(path->mnt); + size_t xattr_stream_size; + char *xattr_stream_name; + int rc; + + rc = ksmbd_vfs_xattr_stream_name(stream_name, + &xattr_stream_name, + &xattr_stream_size, + s_type); + if (rc) + return rc; + + fp->stream.name = xattr_stream_name; + fp->stream.size = xattr_stream_size; + + /* Check if there is stream prefix in xattr space */ + rc = ksmbd_vfs_casexattr_len(user_ns, + path->dentry, + xattr_stream_name, + xattr_stream_size); + if (rc >= 0) + return 0; + + if (fp->cdoption == FILE_OPEN_LE) { + ksmbd_debug(SMB, "XATTR stream name lookup failed: %d\n", rc); + return -EBADF; + } + + rc = ksmbd_vfs_setxattr(user_ns, path->dentry, + xattr_stream_name, NULL, 0, 0); + if (rc < 0) + pr_err("Failed to store XATTR stream name :%d\n", rc); + return 0; +} + +static int smb2_remove_smb_xattrs(const struct path *path) +{ + struct user_namespace *user_ns = mnt_user_ns(path->mnt); + char *name, *xattr_list = NULL; + ssize_t xattr_list_len; + int err = 0; + + xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list); + if (xattr_list_len < 0) { + goto out; + } else if (!xattr_list_len) { + ksmbd_debug(SMB, "empty xattr in the file\n"); + goto out; + } + + for (name = xattr_list; name - xattr_list < xattr_list_len; + name += strlen(name) + 1) { + ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); + + if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) && + !strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX, + STREAM_PREFIX_LEN)) { + err = ksmbd_vfs_remove_xattr(user_ns, path->dentry, + name); + if (err) + ksmbd_debug(SMB, "remove xattr failed : %s\n", + name); + } + } +out: + kvfree(xattr_list); + return err; +} + +static int smb2_create_truncate(const struct path *path) +{ + int rc = vfs_truncate(path, 0); + + if (rc) { + pr_err("vfs_truncate failed, rc %d\n", rc); + return rc; + } + + rc = smb2_remove_smb_xattrs(path); + if (rc == -EOPNOTSUPP) + rc = 0; + if (rc) + ksmbd_debug(SMB, + "ksmbd_truncate_stream_name_xattr failed, rc %d\n", + rc); + return rc; +} + +static void smb2_new_xattrs(struct ksmbd_tree_connect *tcon, const struct path *path, + struct ksmbd_file *fp) +{ + struct xattr_dos_attrib da = {0}; + int rc; + + if (!test_share_config_flag(tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) + return; + + da.version = 4; + da.attr = le32_to_cpu(fp->f_ci->m_fattr); + da.itime = da.create_time = fp->create_time; + da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | + XATTR_DOSINFO_ITIME; + + rc = ksmbd_vfs_set_dos_attrib_xattr(mnt_user_ns(path->mnt), + path->dentry, &da); + if (rc) + ksmbd_debug(SMB, "failed to store file attribute into xattr\n"); +} + +static void smb2_update_xattrs(struct ksmbd_tree_connect *tcon, + const struct path *path, struct ksmbd_file *fp) +{ + struct xattr_dos_attrib da; + int rc; + + fp->f_ci->m_fattr &= ~(ATTR_HIDDEN_LE | ATTR_SYSTEM_LE); + + /* get FileAttributes from XATTR_NAME_DOS_ATTRIBUTE */ + if (!test_share_config_flag(tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) + return; + + rc = ksmbd_vfs_get_dos_attrib_xattr(mnt_user_ns(path->mnt), + path->dentry, &da); + if (rc > 0) { + fp->f_ci->m_fattr = cpu_to_le32(da.attr); + fp->create_time = da.create_time; + fp->itime = da.itime; + } +} + +static int smb2_creat(struct ksmbd_work *work, struct path *path, char *name, + int open_flags, umode_t posix_mode, bool is_dir) +{ + struct ksmbd_tree_connect *tcon = work->tcon; + struct ksmbd_share_config *share = tcon->share_conf; + umode_t mode; + int rc; + + if (!(open_flags & O_CREAT)) + return -EBADF; + + ksmbd_debug(SMB, "file does not exist, so creating\n"); + if (is_dir == true) { + ksmbd_debug(SMB, "creating directory\n"); + + mode = share_config_directory_mode(share, posix_mode); + rc = ksmbd_vfs_mkdir(work, name, mode); + if (rc) + return rc; + } else { + ksmbd_debug(SMB, "creating regular file\n"); + + mode = share_config_create_mode(share, posix_mode); + rc = ksmbd_vfs_create(work, name, mode); + if (rc) + return rc; + } + + rc = ksmbd_vfs_kern_path(work, name, 0, path, 0); + if (rc) { + pr_err("cannot get linux path (%s), err = %d\n", + name, rc); + return rc; + } + return 0; +} + +static int smb2_create_sd_buffer(struct ksmbd_work *work, + struct smb2_create_req *req, + const struct path *path) +{ + struct create_context *context; + struct create_sd_buf_req *sd_buf; + + if (!req->CreateContextsOffset) + return -ENOENT; + + /* Parse SD BUFFER create contexts */ + context = smb2_find_context_vals(req, SMB2_CREATE_SD_BUFFER); + if (!context) + return -ENOENT; + else if (IS_ERR(context)) + return PTR_ERR(context); + + ksmbd_debug(SMB, + "Set ACLs using SMB2_CREATE_SD_BUFFER context\n"); + sd_buf = (struct create_sd_buf_req *)context; + if (le16_to_cpu(context->DataOffset) + + le32_to_cpu(context->DataLength) < + sizeof(struct create_sd_buf_req)) + return -EINVAL; + return set_info_sec(work->conn, work->tcon, path, &sd_buf->ntsd, + le32_to_cpu(sd_buf->ccontext.DataLength), true); +} + +static void ksmbd_acls_fattr(struct smb_fattr *fattr, + struct user_namespace *mnt_userns, + struct inode *inode) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + vfsuid_t vfsuid = i_uid_into_vfsuid(mnt_userns, inode); + vfsgid_t vfsgid = i_gid_into_vfsgid(mnt_userns, inode); + + fattr->cf_uid = vfsuid_into_kuid(vfsuid); + fattr->cf_gid = vfsgid_into_kgid(vfsgid); +#else + fattr->cf_uid = i_uid_into_mnt(mnt_userns, inode); + fattr->cf_gid = i_gid_into_mnt(mnt_userns, inode); +#endif +#else + fattr->cf_uid = inode->i_uid; + fattr->cf_gid = inode->i_gid; +#endif + fattr->cf_mode = inode->i_mode; + fattr->cf_acls = NULL; + fattr->cf_dacls = NULL; + + if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 2, 0) + fattr->cf_acls = get_inode_acl(inode, ACL_TYPE_ACCESS); +#else + fattr->cf_acls = get_acl(inode, ACL_TYPE_ACCESS); +#endif + if (S_ISDIR(inode->i_mode)) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 2, 0) + fattr->cf_dacls = get_inode_acl(inode, ACL_TYPE_DEFAULT); +#else + fattr->cf_dacls = get_acl(inode, ACL_TYPE_DEFAULT); +#endif + } +} + +/** + * smb2_open() - handler for smb file open request + * @work: smb work containing request buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_open(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + struct ksmbd_tree_connect *tcon = work->tcon; + struct smb2_create_req *req; + struct smb2_create_rsp *rsp; + struct path path; + struct ksmbd_share_config *share = tcon->share_conf; + struct ksmbd_file *fp = NULL; + struct file *filp = NULL; + struct user_namespace *user_ns = NULL; + struct kstat stat; + struct create_context *context; + struct lease_ctx_info *lc = NULL; + struct create_ea_buf_req *ea_buf = NULL; + struct oplock_info *opinfo; + __le32 *next_ptr = NULL; + int req_op_level = 0, open_flags = 0, may_flags = 0, file_info = 0; + int rc = 0; + int contxt_cnt = 0, query_disk_id = 0; + int maximal_access_ctxt = 0, posix_ctxt = 0; + int s_type = 0; + int next_off = 0; + char *name = NULL; + char *stream_name = NULL; + bool file_present = false, created = false, already_permitted = false; + int share_ret, need_truncate = 0; + u64 time; + umode_t posix_mode = 0; + __le32 daccess, maximal_access = 0; + + WORK_BUFFERS(work, req, rsp); + + if (req->hdr.NextCommand && !work->next_smb2_rcv_hdr_off && + (req->hdr.Flags & SMB2_FLAGS_RELATED_OPERATIONS)) { + ksmbd_debug(SMB, "invalid flag in chained command\n"); + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + smb2_set_err_rsp(work); + return -EINVAL; + } + + if (test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "IPC pipe create request\n"); + return create_smb2_pipe(work); + } + + if (req->NameLength) { + if ((req->CreateOptions & FILE_DIRECTORY_FILE_LE) && + *(char *)req->Buffer == '\\') { + pr_err("not allow directory name included leading slash\n"); + rc = -EINVAL; + goto err_out1; + } + + name = smb2_get_name(share, + req->Buffer, + le16_to_cpu(req->NameLength), + work->conn->local_nls); + if (IS_ERR(name)) { + rc = PTR_ERR(name); + if (rc != -ENOMEM) + rc = -ENOENT; + name = NULL; + goto err_out1; + } + + ksmbd_debug(SMB, "converted name = %s\n", name); + if (strchr(name, ':')) { + if (!test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STREAMS)) { + rc = -EBADF; + goto err_out1; + } + rc = parse_stream_name(name, &stream_name, &s_type); + if (rc < 0) + goto err_out1; + } + + rc = ksmbd_validate_filename(name); + if (rc < 0) + goto err_out1; + + if (ksmbd_share_veto_filename(share, name)) { + rc = -ENOENT; + ksmbd_debug(SMB, "Reject open(), vetoed file: %s\n", + name); + goto err_out1; + } + } else { + name = kstrdup("", GFP_KERNEL); + if (!name) { + rc = -ENOMEM; + goto err_out1; + } + } + + req_op_level = req->RequestedOplockLevel; + if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) + lc = parse_lease_state(req); + + if (le32_to_cpu(req->ImpersonationLevel) > le32_to_cpu(IL_DELEGATE_LE)) { + pr_err("Invalid impersonationlevel : 0x%x\n", + le32_to_cpu(req->ImpersonationLevel)); + rc = -EIO; + rsp->hdr.Status = STATUS_BAD_IMPERSONATION_LEVEL; + goto err_out1; + } + + if (req->CreateOptions && !(req->CreateOptions & CREATE_OPTIONS_MASK)) { + pr_err("Invalid create options : 0x%x\n", + le32_to_cpu(req->CreateOptions)); + rc = -EINVAL; + goto err_out1; + } else { + if (req->CreateOptions & FILE_SEQUENTIAL_ONLY_LE && + req->CreateOptions & FILE_RANDOM_ACCESS_LE) + req->CreateOptions = ~(FILE_SEQUENTIAL_ONLY_LE); + + if (req->CreateOptions & + (FILE_OPEN_BY_FILE_ID_LE | CREATE_TREE_CONNECTION | + FILE_RESERVE_OPFILTER_LE)) { + rc = -EOPNOTSUPP; + goto err_out1; + } + + if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { + if (req->CreateOptions & FILE_NON_DIRECTORY_FILE_LE) { + rc = -EINVAL; + goto err_out1; + } else if (req->CreateOptions & FILE_NO_COMPRESSION_LE) { + req->CreateOptions = ~(FILE_NO_COMPRESSION_LE); + } + } + } + + if (le32_to_cpu(req->CreateDisposition) > + le32_to_cpu(FILE_OVERWRITE_IF_LE)) { + pr_err("Invalid create disposition : 0x%x\n", + le32_to_cpu(req->CreateDisposition)); + rc = -EINVAL; + goto err_out1; + } + + if (!(req->DesiredAccess & DESIRED_ACCESS_MASK)) { + pr_err("Invalid desired access : 0x%x\n", + le32_to_cpu(req->DesiredAccess)); + rc = -EACCES; + goto err_out1; + } + + if (req->FileAttributes && !(req->FileAttributes & ATTR_MASK_LE)) { + pr_err("Invalid file attribute : 0x%x\n", + le32_to_cpu(req->FileAttributes)); + rc = -EINVAL; + goto err_out1; + } + + if (req->CreateContextsOffset) { + /* Parse non-durable handle create contexts */ + context = smb2_find_context_vals(req, SMB2_CREATE_EA_BUFFER); + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out1; + } else if (context) { + ea_buf = (struct create_ea_buf_req *)context; + if (le16_to_cpu(context->DataOffset) + + le32_to_cpu(context->DataLength) < + sizeof(struct create_ea_buf_req)) { + rc = -EINVAL; + goto err_out1; + } + if (req->CreateOptions & FILE_NO_EA_KNOWLEDGE_LE) { + rsp->hdr.Status = STATUS_ACCESS_DENIED; + rc = -EACCES; + goto err_out1; + } + } + + context = smb2_find_context_vals(req, + SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST); + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out1; + } else if (context) { + ksmbd_debug(SMB, + "get query maximal access context\n"); + maximal_access_ctxt = 1; + } + + context = smb2_find_context_vals(req, + SMB2_CREATE_TIMEWARP_REQUEST); + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out1; + } else if (context) { + ksmbd_debug(SMB, "get timewarp context\n"); + rc = -EBADF; + goto err_out1; + } + + if (tcon->posix_extensions) { + context = smb2_find_context_vals(req, + SMB2_CREATE_TAG_POSIX); + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out1; + } else if (context) { + struct create_posix *posix = + (struct create_posix *)context; + if (le16_to_cpu(context->DataOffset) + + le32_to_cpu(context->DataLength) < + sizeof(struct create_posix) - 4) { + rc = -EINVAL; + goto err_out1; + } + ksmbd_debug(SMB, "get posix context\n"); + + posix_mode = le32_to_cpu(posix->Mode); + posix_ctxt = 1; + } + } + } + + if (ksmbd_override_fsids(work)) { + rc = -ENOMEM; + goto err_out1; + } + + rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, 1); + if (!rc) { + if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) { + /* + * If file exists with under flags, return access + * denied error. + */ + if (req->CreateDisposition == FILE_OVERWRITE_IF_LE || + req->CreateDisposition == FILE_OPEN_IF_LE) { + rc = -EACCES; + path_put(&path); + goto err_out; + } + + if (!test_tree_conn_flag(tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + rc = -EACCES; + path_put(&path); + goto err_out; + } + } else if (d_is_symlink(path.dentry)) { + rc = -EACCES; + path_put(&path); + goto err_out; + } + } + + if (rc) { + if (rc != -ENOENT) + goto err_out; + ksmbd_debug(SMB, "can not get linux path for %s, rc = %d\n", + name, rc); + rc = 0; + } else { + file_present = true; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + user_ns = mnt_user_ns(path.mnt); +#else + user_ns = NULL; +#endif + } + if (stream_name) { + if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { + if (s_type == DATA_STREAM) { + rc = -EIO; + rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; + } + } else { + if (file_present && S_ISDIR(d_inode(path.dentry)->i_mode) && + s_type == DATA_STREAM) { + rc = -EIO; + rsp->hdr.Status = STATUS_FILE_IS_A_DIRECTORY; + } + } + + if (req->CreateOptions & FILE_DIRECTORY_FILE_LE && + req->FileAttributes & ATTR_NORMAL_LE) { + rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; + rc = -EIO; + } + + if (rc < 0) + goto err_out; + } + + if (file_present && req->CreateOptions & FILE_NON_DIRECTORY_FILE_LE && + S_ISDIR(d_inode(path.dentry)->i_mode) && + !(req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { + ksmbd_debug(SMB, "open() argument is a directory: %s, %x\n", + name, req->CreateOptions); + rsp->hdr.Status = STATUS_FILE_IS_A_DIRECTORY; + rc = -EIO; + goto err_out; + } + + if (file_present && (req->CreateOptions & FILE_DIRECTORY_FILE_LE) && + !(req->CreateDisposition == FILE_CREATE_LE) && + !S_ISDIR(d_inode(path.dentry)->i_mode)) { + rsp->hdr.Status = STATUS_NOT_A_DIRECTORY; + rc = -EIO; + goto err_out; + } + + if (!stream_name && file_present && + req->CreateDisposition == FILE_CREATE_LE) { + rc = -EEXIST; + goto err_out; + } + + daccess = smb_map_generic_desired_access(req->DesiredAccess); + + if (file_present && !(req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { + rc = smb_check_perm_dacl(conn, &path, &daccess, + sess->user->uid); + if (rc) + goto err_out; + } + + if (daccess & FILE_MAXIMAL_ACCESS_LE) { + if (!file_present) { + daccess = cpu_to_le32(GENERIC_ALL_FLAGS); + } else { + rc = ksmbd_vfs_query_maximal_access(user_ns, + path.dentry, + &daccess); + if (rc) + goto err_out; + already_permitted = true; + } + maximal_access = daccess; + } + + open_flags = smb2_create_open_flags(file_present, daccess, + req->CreateDisposition, + &may_flags); + + if (!test_tree_conn_flag(tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + if (open_flags & O_CREAT) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + rc = -EACCES; + goto err_out; + } + } + + /*create file if not present */ + if (!file_present) { + rc = smb2_creat(work, &path, name, open_flags, posix_mode, + req->CreateOptions & FILE_DIRECTORY_FILE_LE); + if (rc) { + if (rc == -ENOENT) { + rc = -EIO; + rsp->hdr.Status = STATUS_OBJECT_PATH_NOT_FOUND; + } + goto err_out; + } + + created = true; + user_ns = mnt_user_ns(path.mnt); + if (ea_buf) { + if (le32_to_cpu(ea_buf->ccontext.DataLength) < + sizeof(struct smb2_ea_info)) { + rc = -EINVAL; + goto err_out; + } + + rc = smb2_set_ea(&ea_buf->ea, + le32_to_cpu(ea_buf->ccontext.DataLength), + &path); + if (rc == -EOPNOTSUPP) + rc = 0; + else if (rc) + goto err_out; + } + } else if (!already_permitted) { + /* FILE_READ_ATTRIBUTE is allowed without inode_permission, + * because execute(search) permission on a parent directory, + * is already granted. + */ + if (daccess & ~(FILE_READ_ATTRIBUTES_LE | FILE_READ_CONTROL_LE)) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + rc = inode_permission(user_ns, + d_inode(path.dentry), + may_flags); +#else + rc = inode_permission(d_inode(path.dentry), may_flags); +#endif + if (rc) + goto err_out; + + if ((daccess & FILE_DELETE_LE) || + (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE)) { + rc = ksmbd_vfs_may_delete(user_ns, + path.dentry); + if (rc) + goto err_out; + } + } + } + + rc = ksmbd_query_inode_status(d_inode(path.dentry->d_parent)); + if (rc == KSMBD_INODE_STATUS_PENDING_DELETE) { + rc = -EBUSY; + goto err_out; + } + + rc = 0; + filp = dentry_open(&path, open_flags, current_cred()); + if (IS_ERR(filp)) { + rc = PTR_ERR(filp); + pr_err("dentry open for dir failed, rc %d\n", rc); + goto err_out; + } + + if (file_present) { + if (!(open_flags & O_TRUNC)) + file_info = FILE_OPENED; + else + file_info = FILE_OVERWRITTEN; + + if ((req->CreateDisposition & FILE_CREATE_MASK_LE) == + FILE_SUPERSEDE_LE) + file_info = FILE_SUPERSEDED; + } else if (open_flags & O_CREAT) { + file_info = FILE_CREATED; + } + + ksmbd_vfs_set_fadvise(filp, req->CreateOptions); + + /* Obtain Volatile-ID */ + fp = ksmbd_open_fd(work, filp); + if (IS_ERR(fp)) { + fput(filp); + rc = PTR_ERR(fp); + fp = NULL; + goto err_out; + } + + /* Get Persistent-ID */ + ksmbd_open_durable_fd(fp); + if (!has_file_id(fp->persistent_id)) { + rc = -ENOMEM; + goto err_out; + } + + fp->cdoption = req->CreateDisposition; + fp->daccess = daccess; + fp->saccess = req->ShareAccess; + fp->coption = req->CreateOptions; + + /* Set default windows and posix acls if creating new file */ + if (created) { + int posix_acl_rc; + struct inode *inode = d_inode(path.dentry); + + posix_acl_rc = ksmbd_vfs_inherit_posix_acl(user_ns, + path.dentry, + d_inode(path.dentry->d_parent)); + if (posix_acl_rc) + ksmbd_debug(SMB, "inherit posix acl failed : %d\n", posix_acl_rc); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_ACL_XATTR)) { + rc = smb_inherit_dacl(conn, &path, sess->user->uid, + sess->user->gid); + } + + if (rc) { + rc = smb2_create_sd_buffer(work, req, &path); + if (rc) { + if (posix_acl_rc) + ksmbd_vfs_set_init_posix_acl(user_ns, + path.dentry); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_ACL_XATTR)) { + struct smb_fattr fattr; + struct smb_ntsd *pntsd; + int pntsd_size, ace_num = 0; + + ksmbd_acls_fattr(&fattr, user_ns, inode); + if (fattr.cf_acls) + ace_num = fattr.cf_acls->a_count; + if (fattr.cf_dacls) + ace_num += fattr.cf_dacls->a_count; + + pntsd = kmalloc(sizeof(struct smb_ntsd) + + sizeof(struct smb_sid) * 3 + + sizeof(struct smb_acl) + + sizeof(struct smb_ace) * ace_num * 2, + GFP_KERNEL); + if (!pntsd) + goto err_out; + + rc = build_sec_desc(user_ns, + pntsd, NULL, 0, + OWNER_SECINFO | + GROUP_SECINFO | + DACL_SECINFO, + &pntsd_size, &fattr); + posix_acl_release(fattr.cf_acls); + posix_acl_release(fattr.cf_dacls); + if (rc) { + kfree(pntsd); + goto err_out; + } + + rc = ksmbd_vfs_set_sd_xattr(conn, + user_ns, + path.dentry, + pntsd, + pntsd_size); + kfree(pntsd); + if (rc) + pr_err("failed to store ntacl in xattr : %d\n", + rc); + } + } + } + rc = 0; + } + + if (stream_name) { + rc = smb2_set_stream_name_xattr(&path, + fp, + stream_name, + s_type); + if (rc) + goto err_out; + file_info = FILE_CREATED; + } + + fp->attrib_only = !(req->DesiredAccess & ~(FILE_READ_ATTRIBUTES_LE | + FILE_WRITE_ATTRIBUTES_LE | FILE_SYNCHRONIZE_LE)); + if (!S_ISDIR(file_inode(filp)->i_mode) && open_flags & O_TRUNC && + !fp->attrib_only && !stream_name) { + smb_break_all_oplock(work, fp); + need_truncate = 1; + } + + /* fp should be searchable through ksmbd_inode.m_fp_list + * after daccess, saccess, attrib_only, and stream are + * initialized. + */ + write_lock(&fp->f_ci->m_lock); + list_add(&fp->node, &fp->f_ci->m_fp_list); + write_unlock(&fp->f_ci->m_lock); + + /* Check delete pending among previous fp before oplock break */ + if (ksmbd_inode_pending_delete(fp)) { + rc = -EBUSY; + goto err_out; + } + + share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp); + if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_OPLOCKS) || + (req_op_level == SMB2_OPLOCK_LEVEL_LEASE && + !(conn->vals->capabilities & SMB2_GLOBAL_CAP_LEASING))) { + if (share_ret < 0 && !S_ISDIR(file_inode(fp->filp)->i_mode)) { + rc = share_ret; + goto err_out; + } + } else { + if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) { + req_op_level = smb2_map_lease_to_oplock(lc->req_state); + ksmbd_debug(SMB, + "lease req for(%s) req oplock state 0x%x, lease state 0x%x\n", + name, req_op_level, lc->req_state); + rc = find_same_lease_key(sess, fp->f_ci, lc); + if (rc) + goto err_out; + } else if (open_flags == O_RDONLY && + (req_op_level == SMB2_OPLOCK_LEVEL_BATCH || + req_op_level == SMB2_OPLOCK_LEVEL_EXCLUSIVE)) + req_op_level = SMB2_OPLOCK_LEVEL_II; + + rc = smb_grant_oplock(work, req_op_level, + fp->persistent_id, fp, + le32_to_cpu(req->hdr.Id.SyncId.TreeId), + lc, share_ret); + if (rc < 0) + goto err_out; + } + + if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) + ksmbd_fd_set_delete_on_close(fp, file_info); + + if (need_truncate) { + rc = smb2_create_truncate(&path); + if (rc) + goto err_out; + } + + if (req->CreateContextsOffset) { + struct create_alloc_size_req *az_req; + + az_req = (struct create_alloc_size_req *)smb2_find_context_vals(req, + SMB2_CREATE_ALLOCATION_SIZE); + if (IS_ERR(az_req)) { + rc = PTR_ERR(az_req); + goto err_out; + } else if (az_req) { + loff_t alloc_size; + int err; + + if (le16_to_cpu(az_req->ccontext.DataOffset) + + le32_to_cpu(az_req->ccontext.DataLength) < + sizeof(struct create_alloc_size_req)) { + rc = -EINVAL; + goto err_out; + } + alloc_size = le64_to_cpu(az_req->AllocationSize); + ksmbd_debug(SMB, + "request smb2 create allocate size : %llu\n", + alloc_size); + smb_break_all_levII_oplock(work, fp, 1); + err = vfs_fallocate(fp->filp, FALLOC_FL_KEEP_SIZE, 0, + alloc_size); + if (err < 0) + ksmbd_debug(SMB, + "vfs_fallocate is failed : %d\n", + err); + } + + context = smb2_find_context_vals(req, SMB2_CREATE_QUERY_ON_DISK_ID); + if (IS_ERR(context)) { + rc = PTR_ERR(context); + goto err_out; + } else if (context) { + ksmbd_debug(SMB, "get query on disk id context\n"); + query_disk_id = 1; + } + } + + rc = ksmbd_vfs_getattr(&path, &stat); + if (rc) + goto err_out; + + if (stat.result_mask & STATX_BTIME) + fp->create_time = ksmbd_UnixTimeToNT(stat.btime); + else + fp->create_time = ksmbd_UnixTimeToNT(stat.ctime); + if (req->FileAttributes || fp->f_ci->m_fattr == 0) + fp->f_ci->m_fattr = + cpu_to_le32(smb2_get_dos_mode(&stat, le32_to_cpu(req->FileAttributes))); + + if (!created) + smb2_update_xattrs(tcon, &path, fp); + else + smb2_new_xattrs(tcon, &path, fp); + + memcpy(fp->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); + + rsp->StructureSize = cpu_to_le16(89); + rcu_read_lock(); + opinfo = rcu_dereference(fp->f_opinfo); + rsp->OplockLevel = opinfo != NULL ? opinfo->level : 0; + rcu_read_unlock(); + rsp->Reserved = 0; + rsp->CreateAction = cpu_to_le32(file_info); + rsp->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(stat.atime); + rsp->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.mtime); + rsp->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.ctime); + rsp->ChangeTime = cpu_to_le64(time); + rsp->AllocationSize = S_ISDIR(stat.mode) ? 0 : + cpu_to_le64(stat.blocks << 9); + rsp->EndofFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + rsp->FileAttributes = fp->f_ci->m_fattr; + + rsp->Reserved2 = 0; + + rsp->PersistentFileId = fp->persistent_id; + rsp->VolatileFileId = fp->volatile_id; + + rsp->CreateContextsOffset = 0; + rsp->CreateContextsLength = 0; + inc_rfc1001_len(work->response_buf, 88); /* StructureSize - 1*/ + + /* If lease is request send lease context response */ + if (opinfo && opinfo->is_lease) { + struct create_context *lease_ccontext; + + ksmbd_debug(SMB, "lease granted on(%s) lease state 0x%x\n", + name, opinfo->o_lease->state); + rsp->OplockLevel = SMB2_OPLOCK_LEVEL_LEASE; + + lease_ccontext = (struct create_context *)rsp->Buffer; + contxt_cnt++; + create_lease_buf(rsp->Buffer, opinfo->o_lease); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_lease_size); + inc_rfc1001_len(work->response_buf, + conn->vals->create_lease_size); + next_ptr = &lease_ccontext->Next; + next_off = conn->vals->create_lease_size; + } + + if (maximal_access_ctxt) { + struct create_context *mxac_ccontext; + + if (maximal_access == 0) + ksmbd_vfs_query_maximal_access(user_ns, + path.dentry, + &maximal_access); + mxac_ccontext = (struct create_context *)(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength)); + contxt_cnt++; + create_mxac_rsp_buf(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength), + le32_to_cpu(maximal_access)); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_mxac_size); + inc_rfc1001_len(work->response_buf, + conn->vals->create_mxac_size); + if (next_ptr) + *next_ptr = cpu_to_le32(next_off); + next_ptr = &mxac_ccontext->Next; + next_off = conn->vals->create_mxac_size; + } + + if (query_disk_id) { + struct create_context *disk_id_ccontext; + + disk_id_ccontext = (struct create_context *)(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength)); + contxt_cnt++; + create_disk_id_rsp_buf(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength), + stat.ino, tcon->id); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_disk_id_size); + inc_rfc1001_len(work->response_buf, + conn->vals->create_disk_id_size); + if (next_ptr) + *next_ptr = cpu_to_le32(next_off); + next_ptr = &disk_id_ccontext->Next; + next_off = conn->vals->create_disk_id_size; + } + + if (posix_ctxt) { + contxt_cnt++; + create_posix_rsp_buf(rsp->Buffer + + le32_to_cpu(rsp->CreateContextsLength), + fp); + le32_add_cpu(&rsp->CreateContextsLength, + conn->vals->create_posix_size); + inc_rfc1001_len(work->response_buf, + conn->vals->create_posix_size); + if (next_ptr) + *next_ptr = cpu_to_le32(next_off); + } + + if (contxt_cnt > 0) { + rsp->CreateContextsOffset = + cpu_to_le32(offsetof(struct smb2_create_rsp, Buffer)); + } + +err_out: + if (file_present || created) + path_put(&path); + ksmbd_revert_fsids(work); +err_out1: + if (rc) { + if (rc == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (rc == -EOPNOTSUPP) + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + else if (rc == -EACCES || rc == -ESTALE || rc == -EXDEV) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (rc == -ENOENT) + rsp->hdr.Status = STATUS_OBJECT_NAME_INVALID; + else if (rc == -EPERM) + rsp->hdr.Status = STATUS_SHARING_VIOLATION; + else if (rc == -EBUSY) + rsp->hdr.Status = STATUS_DELETE_PENDING; + else if (rc == -EBADF) + rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; + else if (rc == -ENOEXEC) + rsp->hdr.Status = STATUS_DUPLICATE_OBJECTID; + else if (rc == -ENXIO) + rsp->hdr.Status = STATUS_NO_SUCH_DEVICE; + else if (rc == -EEXIST) + rsp->hdr.Status = STATUS_OBJECT_NAME_COLLISION; + else if (rc == -EMFILE) + rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; + if (!rsp->hdr.Status) + rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; + + if (fp) + ksmbd_fd_put(work, fp); + smb2_set_err_rsp(work); + ksmbd_debug(SMB, "Error response: %x\n", rsp->hdr.Status); + } + + kfree(name); + kfree(lc); + + return 0; +} + +static int readdir_info_level_struct_sz(int info_level) +{ + switch (info_level) { + case FILE_FULL_DIRECTORY_INFORMATION: + return sizeof(struct file_full_directory_info); + case FILE_BOTH_DIRECTORY_INFORMATION: + return sizeof(struct file_both_directory_info); + case FILE_DIRECTORY_INFORMATION: + return sizeof(struct file_directory_info); + case FILE_NAMES_INFORMATION: + return sizeof(struct file_names_info); + case FILEID_FULL_DIRECTORY_INFORMATION: + return sizeof(struct file_id_full_dir_info); + case FILEID_BOTH_DIRECTORY_INFORMATION: + return sizeof(struct file_id_both_directory_info); + case SMB_FIND_FILE_POSIX_INFO: + return sizeof(struct smb2_posix_info); + default: + return -EOPNOTSUPP; + } +} + +static int dentry_name(struct ksmbd_dir_info *d_info, int info_level) +{ + switch (info_level) { + case FILE_FULL_DIRECTORY_INFORMATION: + { + struct file_full_directory_info *ffdinfo; + + ffdinfo = (struct file_full_directory_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(ffdinfo->NextEntryOffset); + d_info->name = ffdinfo->FileName; + d_info->name_len = le32_to_cpu(ffdinfo->FileNameLength); + return 0; + } + case FILE_BOTH_DIRECTORY_INFORMATION: + { + struct file_both_directory_info *fbdinfo; + + fbdinfo = (struct file_both_directory_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(fbdinfo->NextEntryOffset); + d_info->name = fbdinfo->FileName; + d_info->name_len = le32_to_cpu(fbdinfo->FileNameLength); + return 0; + } + case FILE_DIRECTORY_INFORMATION: + { + struct file_directory_info *fdinfo; + + fdinfo = (struct file_directory_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(fdinfo->NextEntryOffset); + d_info->name = fdinfo->FileName; + d_info->name_len = le32_to_cpu(fdinfo->FileNameLength); + return 0; + } + case FILE_NAMES_INFORMATION: + { + struct file_names_info *fninfo; + + fninfo = (struct file_names_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(fninfo->NextEntryOffset); + d_info->name = fninfo->FileName; + d_info->name_len = le32_to_cpu(fninfo->FileNameLength); + return 0; + } + case FILEID_FULL_DIRECTORY_INFORMATION: + { + struct file_id_full_dir_info *dinfo; + + dinfo = (struct file_id_full_dir_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(dinfo->NextEntryOffset); + d_info->name = dinfo->FileName; + d_info->name_len = le32_to_cpu(dinfo->FileNameLength); + return 0; + } + case FILEID_BOTH_DIRECTORY_INFORMATION: + { + struct file_id_both_directory_info *fibdinfo; + + fibdinfo = (struct file_id_both_directory_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(fibdinfo->NextEntryOffset); + d_info->name = fibdinfo->FileName; + d_info->name_len = le32_to_cpu(fibdinfo->FileNameLength); + return 0; + } + case SMB_FIND_FILE_POSIX_INFO: + { + struct smb2_posix_info *posix_info; + + posix_info = (struct smb2_posix_info *)d_info->rptr; + d_info->rptr += le32_to_cpu(posix_info->NextEntryOffset); + d_info->name = posix_info->name; + d_info->name_len = le32_to_cpu(posix_info->name_len); + return 0; + } + default: + return -EINVAL; + } +} + +/** + * smb2_populate_readdir_entry() - encode directory entry in smb2 response + * buffer + * @conn: connection instance + * @info_level: smb information level + * @d_info: structure included variables for query dir + * @ksmbd_kstat: ksmbd wrapper of dirent stat information + * + * if directory has many entries, find first can't read it fully. + * find next might be called multiple times to read remaining dir entries + * + * Return: 0 on success, otherwise error + */ +static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level, + struct ksmbd_dir_info *d_info, + struct ksmbd_kstat *ksmbd_kstat) +{ + int next_entry_offset = 0; + char *conv_name; + int conv_len; + void *kstat; + int struct_sz, rc = 0; + + conv_name = ksmbd_convert_dir_info_name(d_info, + conn->local_nls, + &conv_len); + if (!conv_name) + return -ENOMEM; + + /* Somehow the name has only terminating NULL bytes */ + if (conv_len < 0) { + rc = -EINVAL; + goto free_conv_name; + } + + struct_sz = readdir_info_level_struct_sz(info_level) + conv_len; + next_entry_offset = ALIGN(struct_sz, KSMBD_DIR_INFO_ALIGNMENT); + d_info->last_entry_off_align = next_entry_offset - struct_sz; + + if (next_entry_offset > d_info->out_buf_len) { + d_info->out_buf_len = 0; + rc = -ENOSPC; + goto free_conv_name; + } + + kstat = d_info->wptr; + if (info_level != FILE_NAMES_INFORMATION) + kstat = ksmbd_vfs_init_kstat(&d_info->wptr, ksmbd_kstat); + + switch (info_level) { + case FILE_FULL_DIRECTORY_INFORMATION: + { + struct file_full_directory_info *ffdinfo; + + ffdinfo = (struct file_full_directory_info *)kstat; + ffdinfo->FileNameLength = cpu_to_le32(conv_len); + ffdinfo->EaSize = + smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); + if (ffdinfo->EaSize) + ffdinfo->ExtFileAttributes = ATTR_REPARSE_POINT_LE; + if (d_info->hide_dot_file && d_info->name[0] == '.') + ffdinfo->ExtFileAttributes |= ATTR_HIDDEN_LE; + memcpy(ffdinfo->FileName, conv_name, conv_len); + ffdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_BOTH_DIRECTORY_INFORMATION: + { + struct file_both_directory_info *fbdinfo; + + fbdinfo = (struct file_both_directory_info *)kstat; + fbdinfo->FileNameLength = cpu_to_le32(conv_len); + fbdinfo->EaSize = + smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); + if (fbdinfo->EaSize) + fbdinfo->ExtFileAttributes = ATTR_REPARSE_POINT_LE; + fbdinfo->ShortNameLength = 0; + fbdinfo->Reserved = 0; + if (d_info->hide_dot_file && d_info->name[0] == '.') + fbdinfo->ExtFileAttributes |= ATTR_HIDDEN_LE; + memcpy(fbdinfo->FileName, conv_name, conv_len); + fbdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_DIRECTORY_INFORMATION: + { + struct file_directory_info *fdinfo; + + fdinfo = (struct file_directory_info *)kstat; + fdinfo->FileNameLength = cpu_to_le32(conv_len); + if (d_info->hide_dot_file && d_info->name[0] == '.') + fdinfo->ExtFileAttributes |= ATTR_HIDDEN_LE; + memcpy(fdinfo->FileName, conv_name, conv_len); + fdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_NAMES_INFORMATION: + { + struct file_names_info *fninfo; + + fninfo = (struct file_names_info *)kstat; + fninfo->FileNameLength = cpu_to_le32(conv_len); + memcpy(fninfo->FileName, conv_name, conv_len); + fninfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILEID_FULL_DIRECTORY_INFORMATION: + { + struct file_id_full_dir_info *dinfo; + + dinfo = (struct file_id_full_dir_info *)kstat; + dinfo->FileNameLength = cpu_to_le32(conv_len); + dinfo->EaSize = + smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); + if (dinfo->EaSize) + dinfo->ExtFileAttributes = ATTR_REPARSE_POINT_LE; + dinfo->Reserved = 0; + dinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); + if (d_info->hide_dot_file && d_info->name[0] == '.') + dinfo->ExtFileAttributes |= ATTR_HIDDEN_LE; + memcpy(dinfo->FileName, conv_name, conv_len); + dinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILEID_BOTH_DIRECTORY_INFORMATION: + { + struct file_id_both_directory_info *fibdinfo; + + fibdinfo = (struct file_id_both_directory_info *)kstat; + fibdinfo->FileNameLength = cpu_to_le32(conv_len); + fibdinfo->EaSize = + smb2_get_reparse_tag_special_file(ksmbd_kstat->kstat->mode); + if (fibdinfo->EaSize) + fibdinfo->ExtFileAttributes = ATTR_REPARSE_POINT_LE; + fibdinfo->UniqueId = cpu_to_le64(ksmbd_kstat->kstat->ino); + fibdinfo->ShortNameLength = 0; + fibdinfo->Reserved = 0; + fibdinfo->Reserved2 = cpu_to_le16(0); + if (d_info->hide_dot_file && d_info->name[0] == '.') + fibdinfo->ExtFileAttributes |= ATTR_HIDDEN_LE; + memcpy(fibdinfo->FileName, conv_name, conv_len); + fibdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case SMB_FIND_FILE_POSIX_INFO: + { + struct smb2_posix_info *posix_info; + u64 time; + + posix_info = (struct smb2_posix_info *)kstat; + posix_info->Ignored = 0; + posix_info->CreationTime = cpu_to_le64(ksmbd_kstat->create_time); + time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->ctime); + posix_info->ChangeTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->atime); + posix_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->mtime); + posix_info->LastWriteTime = cpu_to_le64(time); + posix_info->EndOfFile = cpu_to_le64(ksmbd_kstat->kstat->size); + posix_info->AllocationSize = cpu_to_le64(ksmbd_kstat->kstat->blocks << 9); + posix_info->DeviceId = cpu_to_le32(ksmbd_kstat->kstat->rdev); + posix_info->HardLinks = cpu_to_le32(ksmbd_kstat->kstat->nlink); + posix_info->Mode = cpu_to_le32(ksmbd_kstat->kstat->mode & 0777); + posix_info->Inode = cpu_to_le64(ksmbd_kstat->kstat->ino); + posix_info->DosAttributes = + S_ISDIR(ksmbd_kstat->kstat->mode) ? ATTR_DIRECTORY_LE : ATTR_ARCHIVE_LE; + if (d_info->hide_dot_file && d_info->name[0] == '.') + posix_info->DosAttributes |= ATTR_HIDDEN_LE; + /* + * SidBuffer(32) contain two sids(Domain sid(16), UNIX group sid(16)). + * UNIX sid(16) = revision(1) + num_subauth(1) + authority(6) + + * sub_auth(4 * 1(num_subauth)) + RID(4). + */ + id_to_sid(from_kuid_munged(&init_user_ns, ksmbd_kstat->kstat->uid), + SIDUNIX_USER, (struct smb_sid *)&posix_info->SidBuffer[0]); + id_to_sid(from_kgid_munged(&init_user_ns, ksmbd_kstat->kstat->gid), + SIDUNIX_GROUP, (struct smb_sid *)&posix_info->SidBuffer[16]); + memcpy(posix_info->name, conv_name, conv_len); + posix_info->name_len = cpu_to_le32(conv_len); + posix_info->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + + } /* switch (info_level) */ + + d_info->last_entry_offset = d_info->data_count; + d_info->data_count += next_entry_offset; + d_info->out_buf_len -= next_entry_offset; + d_info->wptr += next_entry_offset; + + ksmbd_debug(SMB, + "info_level : %d, buf_len :%d, next_offset : %d, data_count : %d\n", + info_level, d_info->out_buf_len, + next_entry_offset, d_info->data_count); + +free_conv_name: + kfree(conv_name); + return rc; +} + +struct smb2_query_dir_private { + struct ksmbd_work *work; + char *search_pattern; + struct ksmbd_file *dir_fp; + + struct ksmbd_dir_info *d_info; + int info_level; +}; + +static void lock_dir(struct ksmbd_file *dir_fp) +{ + struct dentry *dir = dir_fp->filp->f_path.dentry; + + inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); +} + +static void unlock_dir(struct ksmbd_file *dir_fp) +{ + struct dentry *dir = dir_fp->filp->f_path.dentry; + + inode_unlock(d_inode(dir)); +} + +static int process_query_dir_entries(struct smb2_query_dir_private *priv) +{ + struct user_namespace *user_ns = file_mnt_user_ns(priv->dir_fp->filp); + struct kstat kstat; + struct ksmbd_kstat ksmbd_kstat; + int rc; + int i; + + for (i = 0; i < priv->d_info->num_entry; i++) { + struct dentry *dent; + + if (dentry_name(priv->d_info, priv->info_level)) + return -EINVAL; + + lock_dir(priv->dir_fp); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) + dent = lookup_one(user_ns, priv->d_info->name, + priv->dir_fp->filp->f_path.dentry, + priv->d_info->name_len); +#else + dent = lookup_one_len(priv->d_info->name, + priv->dir_fp->filp->f_path.dentry, + priv->d_info->name_len); +#endif + unlock_dir(priv->dir_fp); + + if (IS_ERR(dent)) { + ksmbd_debug(SMB, "Cannot lookup `%s' [%ld]\n", + priv->d_info->name, + PTR_ERR(dent)); + continue; + } + if (unlikely(d_is_negative(dent))) { + dput(dent); + ksmbd_debug(SMB, "Negative dentry `%s'\n", + priv->d_info->name); + continue; + } + + ksmbd_kstat.kstat = &kstat; + if (priv->info_level != FILE_NAMES_INFORMATION) + ksmbd_vfs_fill_dentry_attrs(priv->work, + user_ns, + dent, + &ksmbd_kstat); + + rc = smb2_populate_readdir_entry(priv->work->conn, + priv->info_level, + priv->d_info, + &ksmbd_kstat); + dput(dent); + if (rc) + return rc; + } + return 0; +} + +static int reserve_populate_dentry(struct ksmbd_dir_info *d_info, + int info_level) +{ + int struct_sz; + int conv_len; + int next_entry_offset; + + struct_sz = readdir_info_level_struct_sz(info_level); + if (struct_sz == -EOPNOTSUPP) + return -EOPNOTSUPP; + + conv_len = (d_info->name_len + 1) * 2; + next_entry_offset = ALIGN(struct_sz + conv_len, + KSMBD_DIR_INFO_ALIGNMENT); + + if (next_entry_offset > d_info->out_buf_len) { + d_info->out_buf_len = 0; + return -ENOSPC; + } + + switch (info_level) { + case FILE_FULL_DIRECTORY_INFORMATION: + { + struct file_full_directory_info *ffdinfo; + + ffdinfo = (struct file_full_directory_info *)d_info->wptr; + memcpy(ffdinfo->FileName, d_info->name, d_info->name_len); + ffdinfo->FileName[d_info->name_len] = 0x00; + ffdinfo->FileNameLength = cpu_to_le32(d_info->name_len); + ffdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_BOTH_DIRECTORY_INFORMATION: + { + struct file_both_directory_info *fbdinfo; + + fbdinfo = (struct file_both_directory_info *)d_info->wptr; + memcpy(fbdinfo->FileName, d_info->name, d_info->name_len); + fbdinfo->FileName[d_info->name_len] = 0x00; + fbdinfo->FileNameLength = cpu_to_le32(d_info->name_len); + fbdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_DIRECTORY_INFORMATION: + { + struct file_directory_info *fdinfo; + + fdinfo = (struct file_directory_info *)d_info->wptr; + memcpy(fdinfo->FileName, d_info->name, d_info->name_len); + fdinfo->FileName[d_info->name_len] = 0x00; + fdinfo->FileNameLength = cpu_to_le32(d_info->name_len); + fdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILE_NAMES_INFORMATION: + { + struct file_names_info *fninfo; + + fninfo = (struct file_names_info *)d_info->wptr; + memcpy(fninfo->FileName, d_info->name, d_info->name_len); + fninfo->FileName[d_info->name_len] = 0x00; + fninfo->FileNameLength = cpu_to_le32(d_info->name_len); + fninfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILEID_FULL_DIRECTORY_INFORMATION: + { + struct file_id_full_dir_info *dinfo; + + dinfo = (struct file_id_full_dir_info *)d_info->wptr; + memcpy(dinfo->FileName, d_info->name, d_info->name_len); + dinfo->FileName[d_info->name_len] = 0x00; + dinfo->FileNameLength = cpu_to_le32(d_info->name_len); + dinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case FILEID_BOTH_DIRECTORY_INFORMATION: + { + struct file_id_both_directory_info *fibdinfo; + + fibdinfo = (struct file_id_both_directory_info *)d_info->wptr; + memcpy(fibdinfo->FileName, d_info->name, d_info->name_len); + fibdinfo->FileName[d_info->name_len] = 0x00; + fibdinfo->FileNameLength = cpu_to_le32(d_info->name_len); + fibdinfo->NextEntryOffset = cpu_to_le32(next_entry_offset); + break; + } + case SMB_FIND_FILE_POSIX_INFO: + { + struct smb2_posix_info *posix_info; + + posix_info = (struct smb2_posix_info *)d_info->wptr; + memcpy(posix_info->name, d_info->name, d_info->name_len); + posix_info->name[d_info->name_len] = 0x00; + posix_info->name_len = cpu_to_le32(d_info->name_len); + posix_info->NextEntryOffset = + cpu_to_le32(next_entry_offset); + break; + } + } /* switch (info_level) */ + + d_info->num_entry++; + d_info->out_buf_len -= next_entry_offset; + d_info->wptr += next_entry_offset; + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) +static bool __query_dir(struct dir_context *ctx, const char *name, int namlen, +#else +static int __query_dir(struct dir_context *ctx, const char *name, int namlen, +#endif + loff_t offset, u64 ino, unsigned int d_type) +{ + struct ksmbd_readdir_data *buf; + struct smb2_query_dir_private *priv; + struct ksmbd_dir_info *d_info; + int rc; + + buf = container_of(ctx, struct ksmbd_readdir_data, ctx); + priv = buf->private; + d_info = priv->d_info; + + /* dot and dotdot entries are already reserved */ + if (!strcmp(".", name) || !strcmp("..", name)) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) + return true; +#else + return 0; +#endif + if (ksmbd_share_veto_filename(priv->work->tcon->share_conf, name)) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) + return true; +#else + return 0; +#endif + if (!match_pattern(name, namlen, priv->search_pattern)) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) + return true; +#else + return 0; +#endif + + d_info->name = name; + d_info->name_len = namlen; + rc = reserve_populate_dentry(d_info, priv->info_level); + if (rc) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) + return false; +#else + return rc; +#endif + if (d_info->flags & SMB2_RETURN_SINGLE_ENTRY) { + d_info->out_buf_len = 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) + return false; +#else + return 0; +#endif + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) + return true; +#else + return 0; +#endif +} + +static int verify_info_level(int info_level) +{ + switch (info_level) { + case FILE_FULL_DIRECTORY_INFORMATION: + case FILE_BOTH_DIRECTORY_INFORMATION: + case FILE_DIRECTORY_INFORMATION: + case FILE_NAMES_INFORMATION: + case FILEID_FULL_DIRECTORY_INFORMATION: + case FILEID_BOTH_DIRECTORY_INFORMATION: + case SMB_FIND_FILE_POSIX_INFO: + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int smb2_resp_buf_len(struct ksmbd_work *work, unsigned short hdr2_len) +{ + int free_len; + + free_len = (int)(work->response_sz - + (get_rfc1002_len(work->response_buf) + 4)) - hdr2_len; + return free_len; +} + +static int smb2_calc_max_out_buf_len(struct ksmbd_work *work, + unsigned short hdr2_len, + unsigned int out_buf_len) +{ + int free_len; + + if (out_buf_len > work->conn->vals->max_trans_size) + return -EINVAL; + + free_len = smb2_resp_buf_len(work, hdr2_len); + if (free_len < 0) + return -EINVAL; + + return min_t(int, out_buf_len, free_len); +} + +int smb2_query_dir(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_query_directory_req *req; + struct smb2_query_directory_rsp *rsp; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct ksmbd_file *dir_fp = NULL; + struct ksmbd_dir_info d_info; + int rc = 0; + char *srch_ptr = NULL; + unsigned char srch_flag; + int buffer_sz; + struct smb2_query_dir_private query_dir_private = {NULL, }; + + WORK_BUFFERS(work, req, rsp); + + if (ksmbd_override_fsids(work)) { + rsp->hdr.Status = STATUS_NO_MEMORY; + smb2_set_err_rsp(work); + return -ENOMEM; + } + + rc = verify_info_level(req->FileInformationClass); + if (rc) { + rc = -EFAULT; + goto err_out2; + } + + dir_fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); + if (!dir_fp) { + rc = -EBADF; + goto err_out2; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + if (!(dir_fp->daccess & FILE_LIST_DIRECTORY_LE) || + inode_permission(file_mnt_user_ns(dir_fp->filp), + file_inode(dir_fp->filp), + MAY_READ | MAY_EXEC)) { +#else + if (!(dir_fp->daccess & FILE_LIST_DIRECTORY_LE) || + inode_permission(file_inode(dir_fp->filp), MAY_READ | MAY_EXEC)) { +#endif + pr_err("no right to enumerate directory (%pD)\n", dir_fp->filp); + rc = -EACCES; + goto err_out2; + } + + if (!S_ISDIR(file_inode(dir_fp->filp)->i_mode)) { + pr_err("can't do query dir for a file\n"); + rc = -EINVAL; + goto err_out2; + } + + srch_flag = req->Flags; + srch_ptr = smb_strndup_from_utf16(req->Buffer, + le16_to_cpu(req->FileNameLength), 1, + conn->local_nls); + if (IS_ERR(srch_ptr)) { + ksmbd_debug(SMB, "Search Pattern not found\n"); + rc = -EINVAL; + goto err_out2; + } else { + ksmbd_debug(SMB, "Search pattern is %s\n", srch_ptr); + } + + if (srch_flag & SMB2_REOPEN || srch_flag & SMB2_RESTART_SCANS) { + ksmbd_debug(SMB, "Restart directory scan\n"); + generic_file_llseek(dir_fp->filp, 0, SEEK_SET); + } + + memset(&d_info, 0, sizeof(struct ksmbd_dir_info)); + d_info.wptr = (char *)rsp->Buffer; + d_info.rptr = (char *)rsp->Buffer; + d_info.out_buf_len = + smb2_calc_max_out_buf_len(work, 8, + le32_to_cpu(req->OutputBufferLength)); + if (d_info.out_buf_len < 0) { + rc = -EINVAL; + goto err_out; + } + d_info.flags = srch_flag; + + /* + * reserve dot and dotdot entries in head of buffer + * in first response + */ + rc = ksmbd_populate_dot_dotdot_entries(work, req->FileInformationClass, + dir_fp, &d_info, srch_ptr, + smb2_populate_readdir_entry); + if (rc == -ENOSPC) + rc = 0; + else if (rc) + goto err_out; + + if (test_share_config_flag(share, KSMBD_SHARE_FLAG_HIDE_DOT_FILES)) + d_info.hide_dot_file = true; + + buffer_sz = d_info.out_buf_len; + d_info.rptr = d_info.wptr; + query_dir_private.work = work; + query_dir_private.search_pattern = srch_ptr; + query_dir_private.dir_fp = dir_fp; + query_dir_private.d_info = &d_info; + query_dir_private.info_level = req->FileInformationClass; + dir_fp->readdir_data.private = &query_dir_private; + set_ctx_actor(&dir_fp->readdir_data.ctx, __query_dir); + + rc = iterate_dir(dir_fp->filp, &dir_fp->readdir_data.ctx); + /* + * req->OutputBufferLength is too small to contain even one entry. + * In this case, it immediately returns OutputBufferLength 0 to client. + */ + if (!d_info.out_buf_len && !d_info.num_entry) + goto no_buf_len; + if (rc > 0 || rc == -ENOSPC) + rc = 0; + else if (rc) + goto err_out; + + d_info.wptr = d_info.rptr; + d_info.out_buf_len = buffer_sz; + rc = process_query_dir_entries(&query_dir_private); + if (rc) + goto err_out; + + if (!d_info.data_count && d_info.out_buf_len >= 0) { + if (srch_flag & SMB2_RETURN_SINGLE_ENTRY && !is_asterisk(srch_ptr)) { + rsp->hdr.Status = STATUS_NO_SUCH_FILE; + } else { + dir_fp->dot_dotdot[0] = dir_fp->dot_dotdot[1] = 0; + rsp->hdr.Status = STATUS_NO_MORE_FILES; + } + rsp->StructureSize = cpu_to_le16(9); + rsp->OutputBufferOffset = cpu_to_le16(0); + rsp->OutputBufferLength = cpu_to_le32(0); + rsp->Buffer[0] = 0; + inc_rfc1001_len(work->response_buf, 9); + } else { +no_buf_len: + ((struct file_directory_info *) + ((char *)rsp->Buffer + d_info.last_entry_offset)) + ->NextEntryOffset = 0; + if (d_info.data_count >= d_info.last_entry_off_align) + d_info.data_count -= d_info.last_entry_off_align; + + rsp->StructureSize = cpu_to_le16(9); + rsp->OutputBufferOffset = cpu_to_le16(72); + rsp->OutputBufferLength = cpu_to_le32(d_info.data_count); + inc_rfc1001_len(work->response_buf, 8 + d_info.data_count); + } + + kfree(srch_ptr); + ksmbd_fd_put(work, dir_fp); + ksmbd_revert_fsids(work); + return 0; + +err_out: + pr_err("error while processing smb2 query dir rc = %d\n", rc); + kfree(srch_ptr); + +err_out2: + if (rc == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (rc == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (rc == -ENOENT) + rsp->hdr.Status = STATUS_NO_SUCH_FILE; + else if (rc == -EBADF) + rsp->hdr.Status = STATUS_FILE_CLOSED; + else if (rc == -ENOMEM) + rsp->hdr.Status = STATUS_NO_MEMORY; + else if (rc == -EFAULT) + rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; + else if (rc == -EIO) + rsp->hdr.Status = STATUS_FILE_CORRUPT_ERROR; + if (!rsp->hdr.Status) + rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; + + smb2_set_err_rsp(work); + ksmbd_fd_put(work, dir_fp); + ksmbd_revert_fsids(work); + return 0; +} + +/** + * buffer_check_err() - helper function to check buffer errors + * @reqOutputBufferLength: max buffer length expected in command response + * @rsp: query info response buffer contains output buffer length + * @rsp_org: base response buffer pointer in case of chained response + * @infoclass_size: query info class response buffer size + * + * Return: 0 on success, otherwise error + */ +static int buffer_check_err(int reqOutputBufferLength, + struct smb2_query_info_rsp *rsp, + void *rsp_org, int infoclass_size) +{ + if (reqOutputBufferLength < le32_to_cpu(rsp->OutputBufferLength)) { + if (reqOutputBufferLength < infoclass_size) { + pr_err("Invalid Buffer Size Requested\n"); + rsp->hdr.Status = STATUS_INFO_LENGTH_MISMATCH; + *(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr)); + return -EINVAL; + } + + ksmbd_debug(SMB, "Buffer Overflow\n"); + rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; + *(__be32 *)rsp_org = cpu_to_be32(sizeof(struct smb2_hdr) + + reqOutputBufferLength); + rsp->OutputBufferLength = cpu_to_le32(reqOutputBufferLength); + } + return 0; +} + +static void get_standard_info_pipe(struct smb2_query_info_rsp *rsp, + void *rsp_org) +{ + struct smb2_file_standard_info *sinfo; + + sinfo = (struct smb2_file_standard_info *)rsp->Buffer; + + sinfo->AllocationSize = cpu_to_le64(4096); + sinfo->EndOfFile = cpu_to_le64(0); + sinfo->NumberOfLinks = cpu_to_le32(1); + sinfo->DeletePending = 1; + sinfo->Directory = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_standard_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_standard_info)); +} + +static void get_internal_info_pipe(struct smb2_query_info_rsp *rsp, u64 num, + void *rsp_org) +{ + struct smb2_file_internal_info *file_info; + + file_info = (struct smb2_file_internal_info *)rsp->Buffer; + + /* any unique number */ + file_info->IndexNumber = cpu_to_le64(num | (1ULL << 63)); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_internal_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_internal_info)); +} + +static int smb2_get_info_file_pipe(struct ksmbd_session *sess, + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp, + void *rsp_org) +{ + u64 id; + int rc; + + /* + * Windows can sometime send query file info request on + * pipe without opening it, checking error condition here + */ + id = req->VolatileFileId; + if (!ksmbd_session_rpc_method(sess, id)) + return -ENOENT; + + ksmbd_debug(SMB, "FileInfoClass %u, FileId 0x%llx\n", + req->FileInfoClass, req->VolatileFileId); + + switch (req->FileInfoClass) { + case FILE_STANDARD_INFORMATION: + get_standard_info_pipe(rsp, rsp_org); + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), + rsp, rsp_org, + FILE_STANDARD_INFORMATION_SIZE); + break; + case FILE_INTERNAL_INFORMATION: + get_internal_info_pipe(rsp, id, rsp_org); + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), + rsp, rsp_org, + FILE_INTERNAL_INFORMATION_SIZE); + break; + default: + ksmbd_debug(SMB, "smb2_info_file_pipe for %u not supported\n", + req->FileInfoClass); + rc = -EOPNOTSUPP; + } + return rc; +} + +/** + * smb2_get_ea() - handler for smb2 get extended attribute command + * @work: smb work containing query info command buffer + * @fp: ksmbd_file pointer + * @req: get extended attribute request + * @rsp: response buffer pointer + * @rsp_org: base response buffer pointer in case of chained response + * + * Return: 0 on success, otherwise error + */ +static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp, void *rsp_org) +{ + struct smb2_ea_info *eainfo, *prev_eainfo; + char *name, *ptr, *xattr_list = NULL, *buf; + int rc, name_len, value_len, xattr_list_len, idx; + ssize_t buf_free_len, alignment_bytes, next_offset, rsp_data_cnt = 0; + struct smb2_ea_info_req *ea_req = NULL; + const struct path *path; + struct user_namespace *user_ns = file_mnt_user_ns(fp->filp); + + if (!(fp->daccess & FILE_READ_EA_LE)) { + pr_err("Not permitted to read ext attr : 0x%x\n", + fp->daccess); + return -EACCES; + } + + path = &fp->filp->f_path; + /* single EA entry is requested with given user.* name */ + if (req->InputBufferLength) { + if (le32_to_cpu(req->InputBufferLength) < + sizeof(struct smb2_ea_info_req)) + return -EINVAL; + + ea_req = (struct smb2_ea_info_req *)req->Buffer; + } else { + /* need to send all EAs, if no specific EA is requested*/ + if (le32_to_cpu(req->Flags) & SL_RETURN_SINGLE_ENTRY) + ksmbd_debug(SMB, + "All EAs are requested but need to send single EA entry in rsp flags 0x%x\n", + le32_to_cpu(req->Flags)); + } + + buf_free_len = + smb2_calc_max_out_buf_len(work, 8, + le32_to_cpu(req->OutputBufferLength)); + if (buf_free_len < 0) + return -EINVAL; + + rc = ksmbd_vfs_listxattr(path->dentry, &xattr_list); + if (rc < 0) { + rsp->hdr.Status = STATUS_INVALID_HANDLE; + goto out; + } else if (!rc) { /* there is no EA in the file */ + ksmbd_debug(SMB, "no ea data in the file\n"); + goto done; + } + xattr_list_len = rc; + + ptr = (char *)rsp->Buffer; + eainfo = (struct smb2_ea_info *)ptr; + prev_eainfo = eainfo; + idx = 0; + + while (idx < xattr_list_len) { + name = xattr_list + idx; + name_len = strlen(name); + + ksmbd_debug(SMB, "%s, len %d\n", name, name_len); + idx += name_len + 1; + + /* + * CIFS does not support EA other than user.* namespace, + * still keep the framework generic, to list other attrs + * in future. + */ + if (strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) + continue; + + if (!strncmp(&name[XATTR_USER_PREFIX_LEN], STREAM_PREFIX, + STREAM_PREFIX_LEN)) + continue; + + if (req->InputBufferLength && + strncmp(&name[XATTR_USER_PREFIX_LEN], ea_req->name, + ea_req->EaNameLength)) + continue; + + if (!strncmp(&name[XATTR_USER_PREFIX_LEN], + DOS_ATTRIBUTE_PREFIX, DOS_ATTRIBUTE_PREFIX_LEN)) + continue; + + if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) + name_len -= XATTR_USER_PREFIX_LEN; + + ptr = (char *)(&eainfo->name + name_len + 1); + buf_free_len -= (offsetof(struct smb2_ea_info, name) + + name_len + 1); + /* bailout if xattr can't fit in buf_free_len */ + value_len = ksmbd_vfs_getxattr(user_ns, path->dentry, + name, &buf); + if (value_len <= 0) { + rc = -ENOENT; + rsp->hdr.Status = STATUS_INVALID_HANDLE; + goto out; + } + + buf_free_len -= value_len; + if (buf_free_len < 0) { + kfree(buf); + break; + } + + memcpy(ptr, buf, value_len); + kfree(buf); + + ptr += value_len; + eainfo->Flags = 0; + eainfo->EaNameLength = name_len; + + if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) + memcpy(eainfo->name, &name[XATTR_USER_PREFIX_LEN], + name_len); + else + memcpy(eainfo->name, name, name_len); + + eainfo->name[name_len] = '\0'; + eainfo->EaValueLength = cpu_to_le16(value_len); + next_offset = offsetof(struct smb2_ea_info, name) + + name_len + 1 + value_len; + + /* align next xattr entry at 4 byte bundary */ + alignment_bytes = ((next_offset + 3) & ~3) - next_offset; + if (alignment_bytes) { + memset(ptr, '\0', alignment_bytes); + ptr += alignment_bytes; + next_offset += alignment_bytes; + buf_free_len -= alignment_bytes; + } + eainfo->NextEntryOffset = cpu_to_le32(next_offset); + prev_eainfo = eainfo; + eainfo = (struct smb2_ea_info *)ptr; + rsp_data_cnt += next_offset; + + if (req->InputBufferLength) { + ksmbd_debug(SMB, "single entry requested\n"); + break; + } + } + + /* no more ea entries */ + prev_eainfo->NextEntryOffset = 0; +done: + rc = 0; + if (rsp_data_cnt == 0) + rsp->hdr.Status = STATUS_NO_EAS_ON_FILE; + rsp->OutputBufferLength = cpu_to_le32(rsp_data_cnt); + inc_rfc1001_len(rsp_org, rsp_data_cnt); +out: + kvfree(xattr_list); + return rc; +} + +static void get_file_access_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_access_info *file_info; + + file_info = (struct smb2_file_access_info *)rsp->Buffer; + file_info->AccessFlags = fp->daccess; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_access_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_access_info)); +} + +static int get_file_basic_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_basic_info *basic_info; + struct kstat stat; + u64 time; + + if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { + pr_err("no right to read the attributes : 0x%x\n", + fp->daccess); + return -EACCES; + } + + basic_info = (struct smb2_file_basic_info *)rsp->Buffer; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), &stat); +#else + generic_fillattr(file_inode(fp->filp), &stat); +#endif + basic_info->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(stat.atime); + basic_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.mtime); + basic_info->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.ctime); + basic_info->ChangeTime = cpu_to_le64(time); + basic_info->Attributes = fp->f_ci->m_fattr; + basic_info->Pad1 = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_basic_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_basic_info)); + return 0; +} + +static unsigned long long get_allocation_size(struct inode *inode, + struct kstat *stat) +{ + unsigned long long alloc_size = 0; + + if (!S_ISDIR(stat->mode)) { + if ((inode->i_blocks << 9) <= stat->size) + alloc_size = stat->size; + else + alloc_size = inode->i_blocks << 9; + } + + return alloc_size; +} + +static void get_file_standard_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_standard_info *sinfo; + unsigned int delete_pending; + struct inode *inode; + struct kstat stat; + + inode = file_inode(fp->filp); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(file_mnt_user_ns(fp->filp), inode, &stat); +#else + generic_fillattr(inode, &stat); +#endif + + sinfo = (struct smb2_file_standard_info *)rsp->Buffer; + delete_pending = ksmbd_inode_pending_delete(fp); + + sinfo->AllocationSize = cpu_to_le64(get_allocation_size(inode, &stat)); + sinfo->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + sinfo->NumberOfLinks = cpu_to_le32(get_nlink(&stat) - delete_pending); + sinfo->DeletePending = delete_pending; + sinfo->Directory = S_ISDIR(stat.mode) ? 1 : 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_standard_info)); + inc_rfc1001_len(rsp_org, + sizeof(struct smb2_file_standard_info)); +} + +static void get_file_alignment_info(struct smb2_query_info_rsp *rsp, + void *rsp_org) +{ + struct smb2_file_alignment_info *file_info; + + file_info = (struct smb2_file_alignment_info *)rsp->Buffer; + file_info->AlignmentRequirement = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_alignment_info)); + inc_rfc1001_len(rsp_org, + sizeof(struct smb2_file_alignment_info)); +} + +static int get_file_all_info(struct ksmbd_work *work, + struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, + void *rsp_org) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_file_all_info *file_info; + unsigned int delete_pending; + struct inode *inode; + struct kstat stat; + int conv_len; + char *filename; + u64 time; + + if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { + ksmbd_debug(SMB, "no right to read the attributes : 0x%x\n", + fp->daccess); + return -EACCES; + } + + filename = convert_to_nt_pathname(work->tcon->share_conf, &fp->filp->f_path); + if (IS_ERR(filename)) + return PTR_ERR(filename); + + inode = file_inode(fp->filp); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(file_mnt_user_ns(fp->filp), inode, &stat); +#else + generic_fillattr(inode, &stat); +#endif + + ksmbd_debug(SMB, "filename = %s\n", filename); + delete_pending = ksmbd_inode_pending_delete(fp); + file_info = (struct smb2_file_all_info *)rsp->Buffer; + + file_info->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(stat.atime); + file_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.mtime); + file_info->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.ctime); + file_info->ChangeTime = cpu_to_le64(time); + file_info->Attributes = fp->f_ci->m_fattr; + file_info->Pad1 = 0; + file_info->AllocationSize = + cpu_to_le64(get_allocation_size(inode, &stat)); + file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + file_info->NumberOfLinks = + cpu_to_le32(get_nlink(&stat) - delete_pending); + file_info->DeletePending = delete_pending; + file_info->Directory = S_ISDIR(stat.mode) ? 1 : 0; + file_info->Pad2 = 0; + file_info->IndexNumber = cpu_to_le64(stat.ino); + file_info->EASize = 0; + file_info->AccessFlags = fp->daccess; + file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos); + file_info->Mode = fp->coption; + file_info->AlignmentRequirement = 0; + conv_len = smbConvertToUTF16((__le16 *)file_info->FileName, filename, + PATH_MAX, conn->local_nls, 0); + conv_len *= 2; + file_info->FileNameLength = cpu_to_le32(conv_len); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_all_info) + conv_len - 1); + kfree(filename); + inc_rfc1001_len(rsp_org, le32_to_cpu(rsp->OutputBufferLength)); + return 0; +} + +static void get_file_alternate_info(struct ksmbd_work *work, + struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, + void *rsp_org) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_file_alt_name_info *file_info; + struct dentry *dentry = fp->filp->f_path.dentry; + int conv_len; + + spin_lock(&dentry->d_lock); + file_info = (struct smb2_file_alt_name_info *)rsp->Buffer; + conv_len = ksmbd_extract_shortname(conn, + dentry->d_name.name, + file_info->FileName); + spin_unlock(&dentry->d_lock); + file_info->FileNameLength = cpu_to_le32(conv_len); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_alt_name_info) + conv_len); + inc_rfc1001_len(rsp_org, le32_to_cpu(rsp->OutputBufferLength)); +} + +static void get_file_stream_info(struct ksmbd_work *work, + struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, + void *rsp_org) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_file_stream_info *file_info; + char *stream_name, *xattr_list = NULL, *stream_buf; + struct kstat stat; + const struct path *path = &fp->filp->f_path; + ssize_t xattr_list_len; + int nbytes = 0, streamlen, stream_name_len, next, idx = 0; + int buf_free_len; + struct smb2_query_info_req *req = ksmbd_req_buf_next(work); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), &stat); +#else + generic_fillattr(file_inode(fp->filp), &stat); +#endif + file_info = (struct smb2_file_stream_info *)rsp->Buffer; + + buf_free_len = + smb2_calc_max_out_buf_len(work, 8, + le32_to_cpu(req->OutputBufferLength)); + if (buf_free_len < 0) + goto out; + + xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list); + if (xattr_list_len < 0) { + goto out; + } else if (!xattr_list_len) { + ksmbd_debug(SMB, "empty xattr in the file\n"); + goto out; + } + + while (idx < xattr_list_len) { + stream_name = xattr_list + idx; + streamlen = strlen(stream_name); + idx += streamlen + 1; + + ksmbd_debug(SMB, "%s, len %d\n", stream_name, streamlen); + + if (strncmp(&stream_name[XATTR_USER_PREFIX_LEN], + STREAM_PREFIX, STREAM_PREFIX_LEN)) + continue; + + stream_name_len = streamlen - (XATTR_USER_PREFIX_LEN + + STREAM_PREFIX_LEN); + streamlen = stream_name_len; + + /* plus : size */ + streamlen += 1; + stream_buf = kmalloc(streamlen + 1, GFP_KERNEL); + if (!stream_buf) + break; + + streamlen = snprintf(stream_buf, streamlen + 1, + ":%s", &stream_name[XATTR_NAME_STREAM_LEN]); + + next = sizeof(struct smb2_file_stream_info) + streamlen * 2; + if (next > buf_free_len) { + kfree(stream_buf); + break; + } + + file_info = (struct smb2_file_stream_info *)&rsp->Buffer[nbytes]; + streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, + stream_buf, streamlen, + conn->local_nls, 0); + streamlen *= 2; + kfree(stream_buf); + file_info->StreamNameLength = cpu_to_le32(streamlen); + file_info->StreamSize = cpu_to_le64(stream_name_len); + file_info->StreamAllocationSize = cpu_to_le64(stream_name_len); + + nbytes += next; + buf_free_len -= next; + file_info->NextEntryOffset = cpu_to_le32(next); + } + +out: + if (!S_ISDIR(stat.mode) && + buf_free_len >= sizeof(struct smb2_file_stream_info) + 7 * 2) { + file_info = (struct smb2_file_stream_info *) + &rsp->Buffer[nbytes]; + streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName, + "::$DATA", 7, conn->local_nls, 0); + streamlen *= 2; + file_info->StreamNameLength = cpu_to_le32(streamlen); + file_info->StreamSize = cpu_to_le64(stat.size); + file_info->StreamAllocationSize = cpu_to_le64(stat.blocks << 9); + nbytes += sizeof(struct smb2_file_stream_info) + streamlen; + } + + /* last entry offset should be 0 */ + file_info->NextEntryOffset = 0; + kvfree(xattr_list); + + rsp->OutputBufferLength = cpu_to_le32(nbytes); + inc_rfc1001_len(rsp_org, nbytes); +} + +static void get_file_internal_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_internal_info *file_info; + struct kstat stat; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), &stat); +#else + generic_fillattr(file_inode(fp->filp), &stat); +#endif + file_info = (struct smb2_file_internal_info *)rsp->Buffer; + file_info->IndexNumber = cpu_to_le64(stat.ino); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_internal_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_internal_info)); +} + +static int get_file_network_open_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_ntwrk_info *file_info; + struct inode *inode; + struct kstat stat; + u64 time; + + if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { + pr_err("no right to read the attributes : 0x%x\n", + fp->daccess); + return -EACCES; + } + + file_info = (struct smb2_file_ntwrk_info *)rsp->Buffer; + + inode = file_inode(fp->filp); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(file_mnt_user_ns(fp->filp), inode, &stat); +#else + generic_fillattr(inode, &stat); +#endif + + file_info->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(stat.atime); + file_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.mtime); + file_info->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(stat.ctime); + file_info->ChangeTime = cpu_to_le64(time); + file_info->Attributes = fp->f_ci->m_fattr; + file_info->AllocationSize = + cpu_to_le64(get_allocation_size(inode, &stat)); + file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); + file_info->Reserved = cpu_to_le32(0); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_ntwrk_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_ntwrk_info)); + return 0; +} + +static void get_file_ea_info(struct smb2_query_info_rsp *rsp, void *rsp_org) +{ + struct smb2_file_ea_info *file_info; + + file_info = (struct smb2_file_ea_info *)rsp->Buffer; + file_info->EASize = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_ea_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_ea_info)); +} + +static void get_file_position_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_pos_info *file_info; + + file_info = (struct smb2_file_pos_info *)rsp->Buffer; + file_info->CurrentByteOffset = cpu_to_le64(fp->filp->f_pos); + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_pos_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_pos_info)); +} + +static void get_file_mode_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_mode_info *file_info; + + file_info = (struct smb2_file_mode_info *)rsp->Buffer; + file_info->Mode = fp->coption & FILE_MODE_INFO_MASK; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_mode_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_mode_info)); +} + +static void get_file_compression_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_comp_info *file_info; + struct kstat stat; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp), &stat); +#else + generic_fillattr(file_inode(fp->filp), &stat); +#endif + + file_info = (struct smb2_file_comp_info *)rsp->Buffer; + file_info->CompressedFileSize = cpu_to_le64(stat.blocks << 9); + file_info->CompressionFormat = COMPRESSION_FORMAT_NONE; + file_info->CompressionUnitShift = 0; + file_info->ChunkShift = 0; + file_info->ClusterShift = 0; + memset(&file_info->Reserved[0], 0, 3); + + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_comp_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_comp_info)); +} + +static int get_file_attribute_tag_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb2_file_attr_tag_info *file_info; + + if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { + pr_err("no right to read the attributes : 0x%x\n", + fp->daccess); + return -EACCES; + } + + file_info = (struct smb2_file_attr_tag_info *)rsp->Buffer; + file_info->FileAttributes = fp->f_ci->m_fattr; + file_info->ReparseTag = 0; + rsp->OutputBufferLength = + cpu_to_le32(sizeof(struct smb2_file_attr_tag_info)); + inc_rfc1001_len(rsp_org, sizeof(struct smb2_file_attr_tag_info)); + return 0; +} + +static int find_file_posix_info(struct smb2_query_info_rsp *rsp, + struct ksmbd_file *fp, void *rsp_org) +{ + struct smb311_posix_qinfo *file_info; + struct inode *inode = file_inode(fp->filp); + u64 time; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + struct user_namespace *user_ns = file_mnt_user_ns(fp->filp); +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + vfsuid_t vfsuid = i_uid_into_vfsuid(user_ns, inode); + vfsgid_t vfsgid = i_gid_into_vfsgid(user_ns, inode); +#endif + int out_buf_len = sizeof(struct smb311_posix_qinfo) + 32; + + file_info = (struct smb311_posix_qinfo *)rsp->Buffer; + file_info->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(inode->i_atime); + file_info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(inode->i_mtime); + file_info->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(inode->i_ctime); + file_info->ChangeTime = cpu_to_le64(time); + file_info->DosAttributes = fp->f_ci->m_fattr; + file_info->Inode = cpu_to_le64(inode->i_ino); + file_info->EndOfFile = cpu_to_le64(inode->i_size); + file_info->AllocationSize = cpu_to_le64(inode->i_blocks << 9); + file_info->HardLinks = cpu_to_le32(inode->i_nlink); + file_info->Mode = cpu_to_le32(inode->i_mode & 0777); + file_info->DeviceId = cpu_to_le32(inode->i_rdev); + /* + * Sids(32) contain two sids(Domain sid(16), UNIX group sid(16)). + * UNIX sid(16) = revision(1) + num_subauth(1) + authority(6) + + * sub_auth(4 * 1(num_subauth)) + RID(4). + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + id_to_sid(from_kuid_munged(&init_user_ns, vfsuid_into_kuid(vfsuid)), +#else + id_to_sid(from_kuid_munged(&init_user_ns, + i_uid_into_mnt(user_ns, inode)), +#endif +#else + id_to_sid(from_kuid_munged(&init_user_ns, inode->i_uid), +#endif + SIDUNIX_USER, (struct smb_sid *)&file_info->Sids[0]); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + id_to_sid(from_kgid_munged(&init_user_ns, vfsgid_into_kgid(vfsgid)), +#else + id_to_sid(from_kgid_munged(&init_user_ns, + i_gid_into_mnt(user_ns, inode)), +#endif +#else + id_to_sid(from_kgid_munged(&init_user_ns, inode->i_gid), +#endif + SIDUNIX_GROUP, (struct smb_sid *)&file_info->Sids[16]); + + rsp->OutputBufferLength = cpu_to_le32(out_buf_len); + inc_rfc1001_len(rsp_org, out_buf_len); + return out_buf_len; +} + +static int smb2_get_info_file(struct ksmbd_work *work, + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp) +{ + struct ksmbd_file *fp; + int fileinfoclass = 0; + int rc = 0; + int file_infoclass_size; + unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) { + /* smb2 info file called for pipe */ + return smb2_get_info_file_pipe(work->sess, req, rsp, + work->response_buf); + } + + if (work->next_smb2_rcv_hdr_off) { + if (!has_file_id(req->VolatileFileId)) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", + work->compound_fid); + id = work->compound_fid; + pid = work->compound_pfid; + } + } + + if (!has_file_id(id)) { + id = req->VolatileFileId; + pid = req->PersistentFileId; + } + + fp = ksmbd_lookup_fd_slow(work, id, pid); + if (!fp) + return -ENOENT; + + fileinfoclass = req->FileInfoClass; + + switch (fileinfoclass) { + case FILE_ACCESS_INFORMATION: + get_file_access_info(rsp, fp, work->response_buf); + file_infoclass_size = FILE_ACCESS_INFORMATION_SIZE; + break; + + case FILE_BASIC_INFORMATION: + rc = get_file_basic_info(rsp, fp, work->response_buf); + file_infoclass_size = FILE_BASIC_INFORMATION_SIZE; + break; + + case FILE_STANDARD_INFORMATION: + get_file_standard_info(rsp, fp, work->response_buf); + file_infoclass_size = FILE_STANDARD_INFORMATION_SIZE; + break; + + case FILE_ALIGNMENT_INFORMATION: + get_file_alignment_info(rsp, work->response_buf); + file_infoclass_size = FILE_ALIGNMENT_INFORMATION_SIZE; + break; + + case FILE_ALL_INFORMATION: + rc = get_file_all_info(work, rsp, fp, work->response_buf); + file_infoclass_size = FILE_ALL_INFORMATION_SIZE; + break; + + case FILE_ALTERNATE_NAME_INFORMATION: + get_file_alternate_info(work, rsp, fp, work->response_buf); + file_infoclass_size = FILE_ALTERNATE_NAME_INFORMATION_SIZE; + break; + + case FILE_STREAM_INFORMATION: + get_file_stream_info(work, rsp, fp, work->response_buf); + file_infoclass_size = FILE_STREAM_INFORMATION_SIZE; + break; + + case FILE_INTERNAL_INFORMATION: + get_file_internal_info(rsp, fp, work->response_buf); + file_infoclass_size = FILE_INTERNAL_INFORMATION_SIZE; + break; + + case FILE_NETWORK_OPEN_INFORMATION: + rc = get_file_network_open_info(rsp, fp, work->response_buf); + file_infoclass_size = FILE_NETWORK_OPEN_INFORMATION_SIZE; + break; + + case FILE_EA_INFORMATION: + get_file_ea_info(rsp, work->response_buf); + file_infoclass_size = FILE_EA_INFORMATION_SIZE; + break; + + case FILE_FULL_EA_INFORMATION: + rc = smb2_get_ea(work, fp, req, rsp, work->response_buf); + file_infoclass_size = FILE_FULL_EA_INFORMATION_SIZE; + break; + + case FILE_POSITION_INFORMATION: + get_file_position_info(rsp, fp, work->response_buf); + file_infoclass_size = FILE_POSITION_INFORMATION_SIZE; + break; + + case FILE_MODE_INFORMATION: + get_file_mode_info(rsp, fp, work->response_buf); + file_infoclass_size = FILE_MODE_INFORMATION_SIZE; + break; + + case FILE_COMPRESSION_INFORMATION: + get_file_compression_info(rsp, fp, work->response_buf); + file_infoclass_size = FILE_COMPRESSION_INFORMATION_SIZE; + break; + + case FILE_ATTRIBUTE_TAG_INFORMATION: + rc = get_file_attribute_tag_info(rsp, fp, work->response_buf); + file_infoclass_size = FILE_ATTRIBUTE_TAG_INFORMATION_SIZE; + break; + case SMB_FIND_FILE_POSIX_INFO: + if (!work->tcon->posix_extensions) { + pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); + rc = -EOPNOTSUPP; + } else { + file_infoclass_size = find_file_posix_info(rsp, fp, + work->response_buf); + } + break; + default: + ksmbd_debug(SMB, "fileinfoclass %d not supported yet\n", + fileinfoclass); + rc = -EOPNOTSUPP; + } + if (!rc) + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), + rsp, work->response_buf, + file_infoclass_size); + ksmbd_fd_put(work, fp); + return rc; +} + +static int smb2_get_info_filesystem(struct ksmbd_work *work, + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp) +{ + struct ksmbd_session *sess = work->sess; + struct ksmbd_conn *conn = work->conn; + struct ksmbd_share_config *share = work->tcon->share_conf; + int fsinfoclass = 0; + struct kstatfs stfs; + struct path path; + int rc = 0, len; + int fs_infoclass_size = 0; + + rc = kern_path(share->path, LOOKUP_NO_SYMLINKS, &path); + if (rc) { + pr_err("cannot create vfs path\n"); + return -EIO; + } + + rc = vfs_statfs(&path, &stfs); + if (rc) { + pr_err("cannot do stat of path %s\n", share->path); + path_put(&path); + return -EIO; + } + + fsinfoclass = req->FileInfoClass; + + switch (fsinfoclass) { + case FS_DEVICE_INFORMATION: + { + struct filesystem_device_info *info; + + info = (struct filesystem_device_info *)rsp->Buffer; + + info->DeviceType = cpu_to_le32(stfs.f_type); + info->DeviceCharacteristics = cpu_to_le32(0x00000020); + rsp->OutputBufferLength = cpu_to_le32(8); + inc_rfc1001_len(work->response_buf, 8); + fs_infoclass_size = FS_DEVICE_INFORMATION_SIZE; + break; + } + case FS_ATTRIBUTE_INFORMATION: + { + struct filesystem_attribute_info *info; + size_t sz; + + info = (struct filesystem_attribute_info *)rsp->Buffer; + info->Attributes = cpu_to_le32(FILE_SUPPORTS_OBJECT_IDS | + FILE_PERSISTENT_ACLS | + FILE_UNICODE_ON_DISK | + FILE_CASE_PRESERVED_NAMES | + FILE_CASE_SENSITIVE_SEARCH | + FILE_SUPPORTS_BLOCK_REFCOUNTING); + + info->Attributes |= cpu_to_le32(server_conf.share_fake_fscaps); + + info->MaxPathNameComponentLength = cpu_to_le32(stfs.f_namelen); + len = smbConvertToUTF16((__le16 *)info->FileSystemName, + "NTFS", PATH_MAX, conn->local_nls, 0); + len = len * 2; + info->FileSystemNameLen = cpu_to_le32(len); + sz = sizeof(struct filesystem_attribute_info) - 2 + len; + rsp->OutputBufferLength = cpu_to_le32(sz); + inc_rfc1001_len(work->response_buf, sz); + fs_infoclass_size = FS_ATTRIBUTE_INFORMATION_SIZE; + break; + } + case FS_VOLUME_INFORMATION: + { + struct filesystem_vol_info *info; + size_t sz; + unsigned int serial_crc = 0; + + info = (struct filesystem_vol_info *)(rsp->Buffer); + info->VolumeCreationTime = 0; + serial_crc = crc32_le(serial_crc, share->name, + strlen(share->name)); + serial_crc = crc32_le(serial_crc, share->path, + strlen(share->path)); + serial_crc = crc32_le(serial_crc, ksmbd_netbios_name(), + strlen(ksmbd_netbios_name())); + /* Taking dummy value of serial number*/ + info->SerialNumber = cpu_to_le32(serial_crc); + len = smbConvertToUTF16((__le16 *)info->VolumeLabel, + share->name, PATH_MAX, + conn->local_nls, 0); + len = len * 2; + info->VolumeLabelSize = cpu_to_le32(len); + info->Reserved = 0; + sz = sizeof(struct filesystem_vol_info) - 2 + len; + rsp->OutputBufferLength = cpu_to_le32(sz); + inc_rfc1001_len(work->response_buf, sz); + fs_infoclass_size = FS_VOLUME_INFORMATION_SIZE; + break; + } + case FS_SIZE_INFORMATION: + { + struct filesystem_info *info; + + info = (struct filesystem_info *)(rsp->Buffer); + info->TotalAllocationUnits = cpu_to_le64(stfs.f_blocks); + info->FreeAllocationUnits = cpu_to_le64(stfs.f_bfree); + info->SectorsPerAllocationUnit = cpu_to_le32(1); + info->BytesPerSector = cpu_to_le32(stfs.f_bsize); + rsp->OutputBufferLength = cpu_to_le32(24); + inc_rfc1001_len(work->response_buf, 24); + fs_infoclass_size = FS_SIZE_INFORMATION_SIZE; + break; + } + case FS_FULL_SIZE_INFORMATION: + { + struct smb2_fs_full_size_info *info; + + info = (struct smb2_fs_full_size_info *)(rsp->Buffer); + info->TotalAllocationUnits = cpu_to_le64(stfs.f_blocks); + info->CallerAvailableAllocationUnits = + cpu_to_le64(stfs.f_bavail); + info->ActualAvailableAllocationUnits = + cpu_to_le64(stfs.f_bfree); + info->SectorsPerAllocationUnit = cpu_to_le32(1); + info->BytesPerSector = cpu_to_le32(stfs.f_bsize); + rsp->OutputBufferLength = cpu_to_le32(32); + inc_rfc1001_len(work->response_buf, 32); + fs_infoclass_size = FS_FULL_SIZE_INFORMATION_SIZE; + break; + } + case FS_OBJECT_ID_INFORMATION: + { + struct object_id_info *info; + + info = (struct object_id_info *)(rsp->Buffer); + + if (!user_guest(sess->user)) + memcpy(info->objid, user_passkey(sess->user), 16); + else + memset(info->objid, 0, 16); + + info->extended_info.magic = cpu_to_le32(EXTENDED_INFO_MAGIC); + info->extended_info.version = cpu_to_le32(1); + info->extended_info.release = cpu_to_le32(1); + info->extended_info.rel_date = 0; + memcpy(info->extended_info.version_string, "1.1.0", strlen("1.1.0")); + rsp->OutputBufferLength = cpu_to_le32(64); + inc_rfc1001_len(work->response_buf, 64); + fs_infoclass_size = FS_OBJECT_ID_INFORMATION_SIZE; + break; + } + case FS_SECTOR_SIZE_INFORMATION: + { + struct smb3_fs_ss_info *info; + unsigned int sector_size = + min_t(unsigned int, path.mnt->mnt_sb->s_blocksize, 4096); + + info = (struct smb3_fs_ss_info *)(rsp->Buffer); + + info->LogicalBytesPerSector = cpu_to_le32(sector_size); + info->PhysicalBytesPerSectorForAtomicity = + cpu_to_le32(sector_size); + info->PhysicalBytesPerSectorForPerf = cpu_to_le32(sector_size); + info->FSEffPhysicalBytesPerSectorForAtomicity = + cpu_to_le32(sector_size); + info->Flags = cpu_to_le32(SSINFO_FLAGS_ALIGNED_DEVICE | + SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE); + info->ByteOffsetForSectorAlignment = 0; + info->ByteOffsetForPartitionAlignment = 0; + rsp->OutputBufferLength = cpu_to_le32(28); + inc_rfc1001_len(work->response_buf, 28); + fs_infoclass_size = FS_SECTOR_SIZE_INFORMATION_SIZE; + break; + } + case FS_CONTROL_INFORMATION: + { + /* + * TODO : The current implementation is based on + * test result with win7(NTFS) server. It's need to + * modify this to get valid Quota values + * from Linux kernel + */ + struct smb2_fs_control_info *info; + + info = (struct smb2_fs_control_info *)(rsp->Buffer); + info->FreeSpaceStartFiltering = 0; + info->FreeSpaceThreshold = 0; + info->FreeSpaceStopFiltering = 0; + info->DefaultQuotaThreshold = cpu_to_le64(SMB2_NO_FID); + info->DefaultQuotaLimit = cpu_to_le64(SMB2_NO_FID); + info->Padding = 0; + rsp->OutputBufferLength = cpu_to_le32(48); + inc_rfc1001_len(work->response_buf, 48); + fs_infoclass_size = FS_CONTROL_INFORMATION_SIZE; + break; + } + case FS_POSIX_INFORMATION: + { + struct filesystem_posix_info *info; + + if (!work->tcon->posix_extensions) { + pr_err("client doesn't negotiate with SMB3.1.1 POSIX Extensions\n"); + rc = -EOPNOTSUPP; + } else { + info = (struct filesystem_posix_info *)(rsp->Buffer); + info->OptimalTransferSize = cpu_to_le32(stfs.f_bsize); + info->BlockSize = cpu_to_le32(stfs.f_bsize); + info->TotalBlocks = cpu_to_le64(stfs.f_blocks); + info->BlocksAvail = cpu_to_le64(stfs.f_bfree); + info->UserBlocksAvail = cpu_to_le64(stfs.f_bavail); + info->TotalFileNodes = cpu_to_le64(stfs.f_files); + info->FreeFileNodes = cpu_to_le64(stfs.f_ffree); + rsp->OutputBufferLength = cpu_to_le32(56); + inc_rfc1001_len(work->response_buf, 56); + fs_infoclass_size = FS_POSIX_INFORMATION_SIZE; + } + break; + } + default: + path_put(&path); + return -EOPNOTSUPP; + } + rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength), + rsp, work->response_buf, + fs_infoclass_size); + path_put(&path); + return rc; +} + +static int smb2_get_info_sec(struct ksmbd_work *work, + struct smb2_query_info_req *req, + struct smb2_query_info_rsp *rsp) +{ + struct ksmbd_file *fp; + struct user_namespace *user_ns; + struct smb_ntsd *pntsd = (struct smb_ntsd *)rsp->Buffer, *ppntsd = NULL; + struct smb_fattr fattr = {{0}}; + struct inode *inode; + __u32 secdesclen = 0; + unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; + int addition_info = le32_to_cpu(req->AdditionalInformation); + int rc = 0, ppntsd_size = 0; + + if (addition_info & ~(OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO | + PROTECTED_DACL_SECINFO | + UNPROTECTED_DACL_SECINFO)) { + ksmbd_debug(SMB, "Unsupported addition info: 0x%x)\n", + addition_info); + + pntsd->revision = cpu_to_le16(1); + pntsd->type = cpu_to_le16(SELF_RELATIVE | DACL_PROTECTED); + pntsd->osidoffset = 0; + pntsd->gsidoffset = 0; + pntsd->sacloffset = 0; + pntsd->dacloffset = 0; + + secdesclen = sizeof(struct smb_ntsd); + rsp->OutputBufferLength = cpu_to_le32(secdesclen); + inc_rfc1001_len(work->response_buf, secdesclen); + + return 0; + } + + if (work->next_smb2_rcv_hdr_off) { + if (!has_file_id(req->VolatileFileId)) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", + work->compound_fid); + id = work->compound_fid; + pid = work->compound_pfid; + } + } + + if (!has_file_id(id)) { + id = req->VolatileFileId; + pid = req->PersistentFileId; + } + + fp = ksmbd_lookup_fd_slow(work, id, pid); + if (!fp) + return -ENOENT; + + user_ns = file_mnt_user_ns(fp->filp); + inode = file_inode(fp->filp); + ksmbd_acls_fattr(&fattr, user_ns, inode); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_ACL_XATTR)) + ppntsd_size = ksmbd_vfs_get_sd_xattr(work->conn, user_ns, + fp->filp->f_path.dentry, + &ppntsd); + + /* Check if sd buffer size exceeds response buffer size */ + if (smb2_resp_buf_len(work, 8) > ppntsd_size) + rc = build_sec_desc(user_ns, pntsd, ppntsd, ppntsd_size, + addition_info, &secdesclen, &fattr); + posix_acl_release(fattr.cf_acls); + posix_acl_release(fattr.cf_dacls); + kfree(ppntsd); + ksmbd_fd_put(work, fp); + if (rc) + return rc; + + rsp->OutputBufferLength = cpu_to_le32(secdesclen); + inc_rfc1001_len(work->response_buf, secdesclen); + return 0; +} + +/** + * smb2_query_info() - handler for smb2 query info command + * @work: smb work containing query info request buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_query_info(struct ksmbd_work *work) +{ + struct smb2_query_info_req *req; + struct smb2_query_info_rsp *rsp; + int rc = 0; + + WORK_BUFFERS(work, req, rsp); + + ksmbd_debug(SMB, "GOT query info request\n"); + + switch (req->InfoType) { + case SMB2_O_INFO_FILE: + ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n"); + rc = smb2_get_info_file(work, req, rsp); + break; + case SMB2_O_INFO_FILESYSTEM: + ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILESYSTEM\n"); + rc = smb2_get_info_filesystem(work, req, rsp); + break; + case SMB2_O_INFO_SECURITY: + ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n"); + rc = smb2_get_info_sec(work, req, rsp); + break; + default: + ksmbd_debug(SMB, "InfoType %d not supported yet\n", + req->InfoType); + rc = -EOPNOTSUPP; + } + + if (rc < 0) { + if (rc == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (rc == -ENOENT) + rsp->hdr.Status = STATUS_FILE_CLOSED; + else if (rc == -EIO) + rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; + else if (rc == -EOPNOTSUPP || rsp->hdr.Status == 0) + rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; + smb2_set_err_rsp(work); + + ksmbd_debug(SMB, "error while processing smb2 query rc = %d\n", + rc); + return rc; + } + rsp->StructureSize = cpu_to_le16(9); + rsp->OutputBufferOffset = cpu_to_le16(72); + inc_rfc1001_len(work->response_buf, 8); + return 0; +} + +/** + * smb2_close_pipe() - handler for closing IPC pipe + * @work: smb work containing close request buffer + * + * Return: 0 + */ +static noinline int smb2_close_pipe(struct ksmbd_work *work) +{ + u64 id; + struct smb2_close_req *req = smb2_get_msg(work->request_buf); + struct smb2_close_rsp *rsp = smb2_get_msg(work->response_buf); + + id = req->VolatileFileId; + ksmbd_session_rpc_close(work->sess, id); + + rsp->StructureSize = cpu_to_le16(60); + rsp->Flags = 0; + rsp->Reserved = 0; + rsp->CreationTime = 0; + rsp->LastAccessTime = 0; + rsp->LastWriteTime = 0; + rsp->ChangeTime = 0; + rsp->AllocationSize = 0; + rsp->EndOfFile = 0; + rsp->Attributes = 0; + inc_rfc1001_len(work->response_buf, 60); + return 0; +} + +/** + * smb2_close() - handler for smb2 close file command + * @work: smb work containing close request buffer + * + * Return: 0 + */ +int smb2_close(struct ksmbd_work *work) +{ + u64 volatile_id = KSMBD_NO_FID; + u64 sess_id; + struct smb2_close_req *req; + struct smb2_close_rsp *rsp; + struct ksmbd_conn *conn = work->conn; + struct ksmbd_file *fp; + struct inode *inode; + u64 time; + int err = 0; + + WORK_BUFFERS(work, req, rsp); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "IPC pipe close request\n"); + return smb2_close_pipe(work); + } + + sess_id = le64_to_cpu(req->hdr.SessionId); + if (req->hdr.Flags & SMB2_FLAGS_RELATED_OPERATIONS) + sess_id = work->compound_sid; + + work->compound_sid = 0; + if (check_session_id(conn, sess_id)) { + work->compound_sid = sess_id; + } else { + rsp->hdr.Status = STATUS_USER_SESSION_DELETED; + if (req->hdr.Flags & SMB2_FLAGS_RELATED_OPERATIONS) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + err = -EBADF; + goto out; + } + + if (work->next_smb2_rcv_hdr_off && + !has_file_id(req->VolatileFileId)) { + if (!has_file_id(work->compound_fid)) { + /* file already closed, return FILE_CLOSED */ + ksmbd_debug(SMB, "file already closed\n"); + rsp->hdr.Status = STATUS_FILE_CLOSED; + err = -EBADF; + goto out; + } else { + ksmbd_debug(SMB, + "Compound request set FID = %llu:%llu\n", + work->compound_fid, + work->compound_pfid); + volatile_id = work->compound_fid; + + /* file closed, stored id is not valid anymore */ + work->compound_fid = KSMBD_NO_FID; + work->compound_pfid = KSMBD_NO_FID; + } + } else { + volatile_id = req->VolatileFileId; + } + ksmbd_debug(SMB, "volatile_id = %llu\n", volatile_id); + + rsp->StructureSize = cpu_to_le16(60); + rsp->Reserved = 0; + + if (req->Flags == SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB) { + fp = ksmbd_lookup_fd_fast(work, volatile_id); + if (!fp) { + err = -ENOENT; + goto out; + } + + inode = file_inode(fp->filp); + rsp->Flags = SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB; + rsp->AllocationSize = S_ISDIR(inode->i_mode) ? 0 : + cpu_to_le64(inode->i_blocks << 9); + rsp->EndOfFile = cpu_to_le64(inode->i_size); + rsp->Attributes = fp->f_ci->m_fattr; + rsp->CreationTime = cpu_to_le64(fp->create_time); + time = ksmbd_UnixTimeToNT(inode->i_atime); + rsp->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(inode->i_mtime); + rsp->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(inode->i_ctime); + rsp->ChangeTime = cpu_to_le64(time); + ksmbd_fd_put(work, fp); + } else { + rsp->Flags = 0; + rsp->AllocationSize = 0; + rsp->EndOfFile = 0; + rsp->Attributes = 0; + rsp->CreationTime = 0; + rsp->LastAccessTime = 0; + rsp->LastWriteTime = 0; + rsp->ChangeTime = 0; + } + + err = ksmbd_close_fd(work, volatile_id); +out: + if (err) { + if (rsp->hdr.Status == 0) + rsp->hdr.Status = STATUS_FILE_CLOSED; + smb2_set_err_rsp(work); + } else { + inc_rfc1001_len(work->response_buf, 60); + } + + return 0; +} + +/** + * smb2_echo() - handler for smb2 echo(ping) command + * @work: smb work containing echo request buffer + * + * Return: 0 + */ +int smb2_echo(struct ksmbd_work *work) +{ + struct smb2_echo_rsp *rsp = smb2_get_msg(work->response_buf); + + rsp->StructureSize = cpu_to_le16(4); + rsp->Reserved = 0; + inc_rfc1001_len(work->response_buf, 4); + return 0; +} + +static int smb2_rename(struct ksmbd_work *work, + struct ksmbd_file *fp, + struct user_namespace *user_ns, + struct smb2_file_rename_info *file_info, + struct nls_table *local_nls) +{ + struct ksmbd_share_config *share = fp->tcon->share_conf; + char *new_name = NULL, *abs_oldname = NULL, *old_name = NULL; + char *pathname = NULL; + struct path path; + bool file_present = true; + int rc; + + ksmbd_debug(SMB, "setting FILE_RENAME_INFO\n"); + pathname = kmalloc(PATH_MAX, GFP_KERNEL); + if (!pathname) + return -ENOMEM; + + abs_oldname = file_path(fp->filp, pathname, PATH_MAX); + if (IS_ERR(abs_oldname)) { + rc = -EINVAL; + goto out; + } + old_name = strrchr(abs_oldname, '/'); + if (old_name && old_name[1] != '\0') { + old_name++; + } else { + ksmbd_debug(SMB, "can't get last component in path %s\n", + abs_oldname); + rc = -ENOENT; + goto out; + } + + new_name = smb2_get_name(share, + file_info->FileName, + le32_to_cpu(file_info->FileNameLength), + local_nls); + if (IS_ERR(new_name)) { + rc = PTR_ERR(new_name); + goto out; + } + + if (strchr(new_name, ':')) { + int s_type; + char *xattr_stream_name, *stream_name = NULL; + size_t xattr_stream_size; + int len; + + rc = parse_stream_name(new_name, &stream_name, &s_type); + if (rc < 0) + goto out; + + len = strlen(new_name); + if (len > 0 && new_name[len - 1] != '/') { + pr_err("not allow base filename in rename\n"); + rc = -ESHARE; + goto out; + } + + rc = ksmbd_vfs_xattr_stream_name(stream_name, + &xattr_stream_name, + &xattr_stream_size, + s_type); + if (rc) + goto out; + + rc = ksmbd_vfs_setxattr(user_ns, + fp->filp->f_path.dentry, + xattr_stream_name, + NULL, 0, 0); + if (rc < 0) { + pr_err("failed to store stream name in xattr: %d\n", + rc); + rc = -EINVAL; + goto out; + } + + goto out; + } + + ksmbd_debug(SMB, "new name %s\n", new_name); + rc = ksmbd_vfs_kern_path(work, new_name, LOOKUP_NO_SYMLINKS, &path, 1); + if (rc) { + if (rc != -ENOENT) + goto out; + file_present = false; + } else { + path_put(&path); + } + + if (ksmbd_share_veto_filename(share, new_name)) { + rc = -ENOENT; + ksmbd_debug(SMB, "Can't rename vetoed file: %s\n", new_name); + goto out; + } + + if (file_info->ReplaceIfExists) { + if (file_present) { + rc = ksmbd_vfs_remove_file(work, new_name); + if (rc) { + if (rc != -ENOTEMPTY) + rc = -EINVAL; + ksmbd_debug(SMB, "cannot delete %s, rc %d\n", + new_name, rc); + goto out; + } + } + } else { + if (file_present && + strncmp(old_name, path.dentry->d_name.name, strlen(old_name))) { + rc = -EEXIST; + ksmbd_debug(SMB, + "cannot rename already existing file\n"); + goto out; + } + } + + rc = ksmbd_vfs_fp_rename(work, fp, new_name); +out: + kfree(pathname); + if (!IS_ERR(new_name)) + kfree(new_name); + return rc; +} + +static int smb2_create_link(struct ksmbd_work *work, + struct ksmbd_share_config *share, + struct smb2_file_link_info *file_info, + unsigned int buf_len, struct file *filp, + struct nls_table *local_nls) +{ + char *link_name = NULL, *target_name = NULL, *pathname = NULL; + struct path path; + bool file_present = true; + int rc; + + if (buf_len < (u64)sizeof(struct smb2_file_link_info) + + le32_to_cpu(file_info->FileNameLength)) + return -EINVAL; + + ksmbd_debug(SMB, "setting FILE_LINK_INFORMATION\n"); + pathname = kmalloc(PATH_MAX, GFP_KERNEL); + if (!pathname) + return -ENOMEM; + + link_name = smb2_get_name(share, + file_info->FileName, + le32_to_cpu(file_info->FileNameLength), + local_nls); + if (IS_ERR(link_name) || S_ISDIR(file_inode(filp)->i_mode)) { + rc = -EINVAL; + goto out; + } + + ksmbd_debug(SMB, "link name is %s\n", link_name); + target_name = file_path(filp, pathname, PATH_MAX); + if (IS_ERR(target_name)) { + rc = -EINVAL; + goto out; + } + + ksmbd_debug(SMB, "target name is %s\n", target_name); + rc = ksmbd_vfs_kern_path(work, link_name, LOOKUP_NO_SYMLINKS, &path, 0); + if (rc) { + if (rc != -ENOENT) + goto out; + file_present = false; + } else { + path_put(&path); + } + + if (file_info->ReplaceIfExists) { + if (file_present) { + rc = ksmbd_vfs_remove_file(work, link_name); + if (rc) { + rc = -EINVAL; + ksmbd_debug(SMB, "cannot delete %s\n", + link_name); + goto out; + } + } + } else { + if (file_present) { + rc = -EEXIST; + ksmbd_debug(SMB, "link already exists\n"); + goto out; + } + } + + rc = ksmbd_vfs_link(work, target_name, link_name); + if (rc) + rc = -EINVAL; +out: + if (!IS_ERR(link_name)) + kfree(link_name); + kfree(pathname); + return rc; +} + +static int set_file_basic_info(struct ksmbd_file *fp, + struct smb2_file_basic_info *file_info, + struct ksmbd_share_config *share) +{ + struct iattr attrs; + struct file *filp; + struct inode *inode; + struct user_namespace *user_ns; + int rc = 0; + + if (!(fp->daccess & FILE_WRITE_ATTRIBUTES_LE)) + return -EACCES; + + attrs.ia_valid = 0; + filp = fp->filp; + inode = file_inode(filp); + user_ns = file_mnt_user_ns(filp); + + if (file_info->CreationTime) + fp->create_time = le64_to_cpu(file_info->CreationTime); + + if (file_info->LastAccessTime) { + attrs.ia_atime = ksmbd_NTtimeToUnix(file_info->LastAccessTime); + attrs.ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET); + } + + attrs.ia_valid |= ATTR_CTIME; + if (file_info->ChangeTime) + attrs.ia_ctime = ksmbd_NTtimeToUnix(file_info->ChangeTime); + else + attrs.ia_ctime = inode->i_ctime; + + if (file_info->LastWriteTime) { + attrs.ia_mtime = ksmbd_NTtimeToUnix(file_info->LastWriteTime); + attrs.ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET); + } + + if (file_info->Attributes) { + if (!S_ISDIR(inode->i_mode) && + file_info->Attributes & ATTR_DIRECTORY_LE) { + pr_err("can't change a file to a directory\n"); + return -EINVAL; + } + + if (!(S_ISDIR(inode->i_mode) && file_info->Attributes == ATTR_NORMAL_LE)) + fp->f_ci->m_fattr = file_info->Attributes | + (fp->f_ci->m_fattr & ATTR_DIRECTORY_LE); + } + + if (test_share_config_flag(share, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS) && + (file_info->CreationTime || file_info->Attributes)) { + struct xattr_dos_attrib da = {0}; + + da.version = 4; + da.itime = fp->itime; + da.create_time = fp->create_time; + da.attr = le32_to_cpu(fp->f_ci->m_fattr); + da.flags = XATTR_DOSINFO_ATTRIB | XATTR_DOSINFO_CREATE_TIME | + XATTR_DOSINFO_ITIME; + + rc = ksmbd_vfs_set_dos_attrib_xattr(user_ns, + filp->f_path.dentry, &da); + if (rc) + ksmbd_debug(SMB, + "failed to restore file attribute in EA\n"); + rc = 0; + } + + if (attrs.ia_valid) { + struct dentry *dentry = filp->f_path.dentry; + struct inode *inode = d_inode(dentry); + + if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) + return -EACCES; + + inode_lock(inode); + inode->i_ctime = attrs.ia_ctime; + attrs.ia_valid &= ~ATTR_CTIME; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + rc = notify_change(user_ns, dentry, &attrs, NULL); +#else + rc = notify_change(dentry, &attrs, NULL); +#endif + inode_unlock(inode); + if (rc) + return -EINVAL; + } + return rc; +} + +static int set_file_allocation_info(struct ksmbd_work *work, + struct ksmbd_file *fp, + struct smb2_file_alloc_info *file_alloc_info) +{ + /* + * TODO : It's working fine only when store dos attributes + * is not yes. need to implement a logic which works + * properly with any smb.conf option + */ + + loff_t alloc_blks; + struct inode *inode; + int rc; + + if (!(fp->daccess & FILE_WRITE_DATA_LE)) + return -EACCES; + + alloc_blks = (le64_to_cpu(file_alloc_info->AllocationSize) + 511) >> 9; + inode = file_inode(fp->filp); + + if (alloc_blks > inode->i_blocks) { + smb_break_all_levII_oplock(work, fp, 1); + rc = vfs_fallocate(fp->filp, FALLOC_FL_KEEP_SIZE, 0, + alloc_blks * 512); + if (rc && rc != -EOPNOTSUPP) { + pr_err("vfs_fallocate is failed : %d\n", rc); + return rc; + } + } else if (alloc_blks < inode->i_blocks) { + loff_t size; + + /* + * Allocation size could be smaller than original one + * which means allocated blocks in file should be + * deallocated. use truncate to cut out it, but inode + * size is also updated with truncate offset. + * inode size is retained by backup inode size. + */ + size = i_size_read(inode); + rc = ksmbd_vfs_truncate(work, fp, alloc_blks * 512); + if (rc) { + pr_err("truncate failed!, err %d\n", rc); + return rc; + } + if (size < alloc_blks * 512) + i_size_write(inode, size); + } + return 0; +} + +static int set_end_of_file_info(struct ksmbd_work *work, struct ksmbd_file *fp, + struct smb2_file_eof_info *file_eof_info) +{ + loff_t newsize; + struct inode *inode; + int rc; + + if (!(fp->daccess & FILE_WRITE_DATA_LE)) + return -EACCES; + + newsize = le64_to_cpu(file_eof_info->EndOfFile); + inode = file_inode(fp->filp); + + /* + * If FILE_END_OF_FILE_INFORMATION of set_info_file is called + * on FAT32 shared device, truncate execution time is too long + * and network error could cause from windows client. because + * truncate of some filesystem like FAT32 fill zero data in + * truncated range. + */ + if (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC) { + ksmbd_debug(SMB, "truncated to newsize %lld\n", newsize); + rc = ksmbd_vfs_truncate(work, fp, newsize); + if (rc) { + ksmbd_debug(SMB, "truncate failed!, err %d\n", rc); + if (rc != -EAGAIN) + rc = -EBADF; + return rc; + } + } + return 0; +} + +static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp, + struct smb2_file_rename_info *rename_info, + unsigned int buf_len) +{ + struct user_namespace *user_ns; + struct ksmbd_file *parent_fp; + struct dentry *parent; + struct dentry *dentry = fp->filp->f_path.dentry; + int ret; + + if (!(fp->daccess & FILE_DELETE_LE)) { + pr_err("no right to delete : 0x%x\n", fp->daccess); + return -EACCES; + } + + if (buf_len < (u64)sizeof(struct smb2_file_rename_info) + + le32_to_cpu(rename_info->FileNameLength)) + return -EINVAL; + + user_ns = file_mnt_user_ns(fp->filp); + if (ksmbd_stream_fd(fp)) + goto next; + + parent = dget_parent(dentry); + ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry); + if (ret) { + dput(parent); + return ret; + } + + parent_fp = ksmbd_lookup_fd_inode(d_inode(parent)); + inode_unlock(d_inode(parent)); + dput(parent); + + if (parent_fp) { + if (parent_fp->daccess & FILE_DELETE_LE) { + pr_err("parent dir is opened with delete access\n"); + ksmbd_fd_put(work, parent_fp); + return -ESHARE; + } + ksmbd_fd_put(work, parent_fp); + } +next: + return smb2_rename(work, fp, user_ns, rename_info, + work->conn->local_nls); +} + +static int set_file_disposition_info(struct ksmbd_file *fp, + struct smb2_file_disposition_info *file_info) +{ + struct inode *inode; + + if (!(fp->daccess & FILE_DELETE_LE)) { + pr_err("no right to delete : 0x%x\n", fp->daccess); + return -EACCES; + } + + inode = file_inode(fp->filp); + if (file_info->DeletePending) { + if (S_ISDIR(inode->i_mode) && + ksmbd_vfs_empty_dir(fp) == -ENOTEMPTY) + return -EBUSY; + ksmbd_set_inode_pending_delete(fp); + } else { + ksmbd_clear_inode_pending_delete(fp); + } + return 0; +} + +static int set_file_position_info(struct ksmbd_file *fp, + struct smb2_file_pos_info *file_info) +{ + loff_t current_byte_offset; + unsigned long sector_size; + struct inode *inode; + + inode = file_inode(fp->filp); + current_byte_offset = le64_to_cpu(file_info->CurrentByteOffset); + sector_size = inode->i_sb->s_blocksize; + + if (current_byte_offset < 0 || + (fp->coption == FILE_NO_INTERMEDIATE_BUFFERING_LE && + current_byte_offset & (sector_size - 1))) { + pr_err("CurrentByteOffset is not valid : %llu\n", + current_byte_offset); + return -EINVAL; + } + + fp->filp->f_pos = current_byte_offset; + return 0; +} + +static int set_file_mode_info(struct ksmbd_file *fp, + struct smb2_file_mode_info *file_info) +{ + __le32 mode; + + mode = file_info->Mode; + + if ((mode & ~FILE_MODE_INFO_MASK) || + (mode & FILE_SYNCHRONOUS_IO_ALERT_LE && + mode & FILE_SYNCHRONOUS_IO_NONALERT_LE)) { + pr_err("Mode is not valid : 0x%x\n", le32_to_cpu(mode)); + return -EINVAL; + } + + /* + * TODO : need to implement consideration for + * FILE_SYNCHRONOUS_IO_ALERT and FILE_SYNCHRONOUS_IO_NONALERT + */ + ksmbd_vfs_set_fadvise(fp->filp, mode); + fp->coption = mode; + return 0; +} + +/** + * smb2_set_info_file() - handler for smb2 set info command + * @work: smb work containing set info command buffer + * @fp: ksmbd_file pointer + * @req: request buffer pointer + * @share: ksmbd_share_config pointer + * + * Return: 0 on success, otherwise error + * TODO: need to implement an error handling for STATUS_INFO_LENGTH_MISMATCH + */ +static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, + struct smb2_set_info_req *req, + struct ksmbd_share_config *share) +{ + unsigned int buf_len = le32_to_cpu(req->BufferLength); + + switch (req->FileInfoClass) { + case FILE_BASIC_INFORMATION: + { + if (buf_len < sizeof(struct smb2_file_basic_info)) + return -EINVAL; + + return set_file_basic_info(fp, (struct smb2_file_basic_info *)req->Buffer, share); + } + case FILE_ALLOCATION_INFORMATION: + { + if (buf_len < sizeof(struct smb2_file_alloc_info)) + return -EINVAL; + + return set_file_allocation_info(work, fp, + (struct smb2_file_alloc_info *)req->Buffer); + } + case FILE_END_OF_FILE_INFORMATION: + { + if (buf_len < sizeof(struct smb2_file_eof_info)) + return -EINVAL; + + return set_end_of_file_info(work, fp, + (struct smb2_file_eof_info *)req->Buffer); + } + case FILE_RENAME_INFORMATION: + { + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + return -EACCES; + } + + if (buf_len < sizeof(struct smb2_file_rename_info)) + return -EINVAL; + + return set_rename_info(work, fp, + (struct smb2_file_rename_info *)req->Buffer, + buf_len); + } + case FILE_LINK_INFORMATION: + { + if (buf_len < sizeof(struct smb2_file_link_info)) + return -EINVAL; + + return smb2_create_link(work, work->tcon->share_conf, + (struct smb2_file_link_info *)req->Buffer, + buf_len, fp->filp, + work->conn->local_nls); + } + case FILE_DISPOSITION_INFORMATION: + { + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + return -EACCES; + } + + if (buf_len < sizeof(struct smb2_file_disposition_info)) + return -EINVAL; + + return set_file_disposition_info(fp, + (struct smb2_file_disposition_info *)req->Buffer); + } + case FILE_FULL_EA_INFORMATION: + { + if (!(fp->daccess & FILE_WRITE_EA_LE)) { + pr_err("Not permitted to write ext attr: 0x%x\n", + fp->daccess); + return -EACCES; + } + + if (buf_len < sizeof(struct smb2_ea_info)) + return -EINVAL; + + return smb2_set_ea((struct smb2_ea_info *)req->Buffer, + buf_len, &fp->filp->f_path); + } + case FILE_POSITION_INFORMATION: + { + if (buf_len < sizeof(struct smb2_file_pos_info)) + return -EINVAL; + + return set_file_position_info(fp, (struct smb2_file_pos_info *)req->Buffer); + } + case FILE_MODE_INFORMATION: + { + if (buf_len < sizeof(struct smb2_file_mode_info)) + return -EINVAL; + + return set_file_mode_info(fp, (struct smb2_file_mode_info *)req->Buffer); + } + } + + pr_err("Unimplemented Fileinfoclass :%d\n", req->FileInfoClass); + return -EOPNOTSUPP; +} + +static int smb2_set_info_sec(struct ksmbd_file *fp, int addition_info, + char *buffer, int buf_len) +{ + struct smb_ntsd *pntsd = (struct smb_ntsd *)buffer; + + fp->saccess |= FILE_SHARE_DELETE_LE; + + return set_info_sec(fp->conn, fp->tcon, &fp->filp->f_path, pntsd, + buf_len, false); +} + +/** + * smb2_set_info() - handler for smb2 set info command handler + * @work: smb work containing set info request buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_set_info(struct ksmbd_work *work) +{ + struct smb2_set_info_req *req; + struct smb2_set_info_rsp *rsp; + struct ksmbd_file *fp; + int rc = 0; + unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; + + ksmbd_debug(SMB, "Received set info request\n"); + + if (work->next_smb2_rcv_hdr_off) { + req = ksmbd_req_buf_next(work); + rsp = ksmbd_resp_buf_next(work); + if (!has_file_id(req->VolatileFileId)) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", + work->compound_fid); + id = work->compound_fid; + pid = work->compound_pfid; + } + } else { + req = smb2_get_msg(work->request_buf); + rsp = smb2_get_msg(work->response_buf); + } + + if (!has_file_id(id)) { + id = req->VolatileFileId; + pid = req->PersistentFileId; + } + + fp = ksmbd_lookup_fd_slow(work, id, pid); + if (!fp) { + ksmbd_debug(SMB, "Invalid id for close: %u\n", id); + rc = -ENOENT; + goto err_out; + } + + switch (req->InfoType) { + case SMB2_O_INFO_FILE: + ksmbd_debug(SMB, "GOT SMB2_O_INFO_FILE\n"); + rc = smb2_set_info_file(work, fp, req, work->tcon->share_conf); + break; + case SMB2_O_INFO_SECURITY: + ksmbd_debug(SMB, "GOT SMB2_O_INFO_SECURITY\n"); + if (ksmbd_override_fsids(work)) { + rc = -ENOMEM; + goto err_out; + } + rc = smb2_set_info_sec(fp, + le32_to_cpu(req->AdditionalInformation), + req->Buffer, + le32_to_cpu(req->BufferLength)); + ksmbd_revert_fsids(work); + break; + default: + rc = -EOPNOTSUPP; + } + + if (rc < 0) + goto err_out; + + rsp->StructureSize = cpu_to_le16(2); + inc_rfc1001_len(work->response_buf, 2); + ksmbd_fd_put(work, fp); + return 0; + +err_out: + if (rc == -EACCES || rc == -EPERM || rc == -EXDEV) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (rc == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (rc == -ESHARE) + rsp->hdr.Status = STATUS_SHARING_VIOLATION; + else if (rc == -ENOENT) + rsp->hdr.Status = STATUS_OBJECT_NAME_INVALID; + else if (rc == -EBUSY || rc == -ENOTEMPTY) + rsp->hdr.Status = STATUS_DIRECTORY_NOT_EMPTY; + else if (rc == -EAGAIN) + rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; + else if (rc == -EBADF || rc == -ESTALE) + rsp->hdr.Status = STATUS_INVALID_HANDLE; + else if (rc == -EEXIST) + rsp->hdr.Status = STATUS_OBJECT_NAME_COLLISION; + else if (rsp->hdr.Status == 0 || rc == -EOPNOTSUPP) + rsp->hdr.Status = STATUS_INVALID_INFO_CLASS; + smb2_set_err_rsp(work); + ksmbd_fd_put(work, fp); + ksmbd_debug(SMB, "error while processing smb2 query rc = %d\n", rc); + return rc; +} + +/** + * smb2_read_pipe() - handler for smb2 read from IPC pipe + * @work: smb work containing read IPC pipe command buffer + * + * Return: 0 on success, otherwise error + */ +static noinline int smb2_read_pipe(struct ksmbd_work *work) +{ + int nbytes = 0, err; + u64 id; + struct ksmbd_rpc_command *rpc_resp; + struct smb2_read_req *req = smb2_get_msg(work->request_buf); + struct smb2_read_rsp *rsp = smb2_get_msg(work->response_buf); + + id = req->VolatileFileId; + + inc_rfc1001_len(work->response_buf, 16); + rpc_resp = ksmbd_rpc_read(work->sess, id); + if (rpc_resp) { + if (rpc_resp->flags != KSMBD_RPC_OK) { + err = -EINVAL; + goto out; + } + + work->aux_payload_buf = + kvmalloc(rpc_resp->payload_sz, GFP_KERNEL | __GFP_ZERO); + if (!work->aux_payload_buf) { + err = -ENOMEM; + goto out; + } + + memcpy(work->aux_payload_buf, rpc_resp->payload, + rpc_resp->payload_sz); + + nbytes = rpc_resp->payload_sz; + work->resp_hdr_sz = get_rfc1002_len(work->response_buf) + 4; + work->aux_payload_sz = nbytes; + kvfree(rpc_resp); + } + + rsp->StructureSize = cpu_to_le16(17); + rsp->DataOffset = 80; + rsp->Reserved = 0; + rsp->DataLength = cpu_to_le32(nbytes); + rsp->DataRemaining = 0; + rsp->Reserved2 = 0; + inc_rfc1001_len(work->response_buf, nbytes); + return 0; + +out: + rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; + smb2_set_err_rsp(work); + kvfree(rpc_resp); + return err; +} + +static int smb2_set_remote_key_for_rdma(struct ksmbd_work *work, + struct smb2_buffer_desc_v1 *desc, + __le32 Channel, + __le16 ChannelInfoLength) +{ + unsigned int i, ch_count; + + if (work->conn->dialect == SMB30_PROT_ID && + Channel != SMB2_CHANNEL_RDMA_V1) + return -EINVAL; + + ch_count = le16_to_cpu(ChannelInfoLength) / sizeof(*desc); + if (ksmbd_debug_types & KSMBD_DEBUG_RDMA) { + for (i = 0; i < ch_count; i++) { + pr_info("RDMA r/w request %#x: token %#x, length %#x\n", + i, + le32_to_cpu(desc[i].token), + le32_to_cpu(desc[i].length)); + } + } + if (!ch_count) + return -EINVAL; + + work->need_invalidate_rkey = + (Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE); + if (Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE) + work->remote_key = le32_to_cpu(desc->token); + return 0; +} + +static ssize_t smb2_read_rdma_channel(struct ksmbd_work *work, + struct smb2_read_req *req, void *data_buf, + size_t length) +{ + int err; + + err = ksmbd_conn_rdma_write(work->conn, data_buf, length, + (struct smb2_buffer_desc_v1 *) + ((char *)req + le16_to_cpu(req->ReadChannelInfoOffset)), + le16_to_cpu(req->ReadChannelInfoLength)); + if (err) + return err; + + return length; +} + +/** + * smb2_read() - handler for smb2 read from file + * @work: smb work containing read command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_read(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_read_req *req; + struct smb2_read_rsp *rsp; + struct ksmbd_file *fp = NULL; + loff_t offset; + size_t length, mincount; + ssize_t nbytes = 0, remain_bytes = 0; + int err = 0; + bool is_rdma_channel = false; + unsigned int max_read_size = conn->vals->max_read_size; + + WORK_BUFFERS(work, req, rsp); + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "IPC pipe read request\n"); + return smb2_read_pipe(work); + } + + if (req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE || + req->Channel == SMB2_CHANNEL_RDMA_V1) { + is_rdma_channel = true; + max_read_size = get_smbd_max_read_write_size(); + } + + if (is_rdma_channel == true) { + unsigned int ch_offset = le16_to_cpu(req->ReadChannelInfoOffset); + + if (ch_offset < offsetof(struct smb2_read_req, Buffer)) { + err = -EINVAL; + goto out; + } + err = smb2_set_remote_key_for_rdma(work, + (struct smb2_buffer_desc_v1 *) + ((char *)req + ch_offset), + req->Channel, + req->ReadChannelInfoLength); + if (err) + goto out; + } + + fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); + if (!fp) { + err = -ENOENT; + goto out; + } + + if (!(fp->daccess & (FILE_READ_DATA_LE | FILE_READ_ATTRIBUTES_LE))) { + pr_err("Not permitted to read : 0x%x\n", fp->daccess); + err = -EACCES; + goto out; + } + + offset = le64_to_cpu(req->Offset); + length = le32_to_cpu(req->Length); + mincount = le32_to_cpu(req->MinimumCount); + + if (length > max_read_size) { + ksmbd_debug(SMB, "limiting read size to max size(%u)\n", + max_read_size); + err = -EINVAL; + goto out; + } + + ksmbd_debug(SMB, "filename %pD, offset %lld, len %zu\n", + fp->filp, offset, length); + + work->aux_payload_buf = kvmalloc(length, GFP_KERNEL | __GFP_ZERO); + if (!work->aux_payload_buf) { + err = -ENOMEM; + goto out; + } + + nbytes = ksmbd_vfs_read(work, fp, length, &offset); + if (nbytes < 0) { + err = nbytes; + goto out; + } + + if ((nbytes == 0 && length != 0) || nbytes < mincount) { + kvfree(work->aux_payload_buf); + work->aux_payload_buf = NULL; + rsp->hdr.Status = STATUS_END_OF_FILE; + smb2_set_err_rsp(work); + ksmbd_fd_put(work, fp); + return 0; + } + + ksmbd_debug(SMB, "nbytes %zu, offset %lld mincount %zu\n", + nbytes, offset, mincount); + + if (is_rdma_channel == true) { + /* write data to the client using rdma channel */ + remain_bytes = smb2_read_rdma_channel(work, req, + work->aux_payload_buf, + nbytes); + kvfree(work->aux_payload_buf); + work->aux_payload_buf = NULL; + + nbytes = 0; + if (remain_bytes < 0) { + err = (int)remain_bytes; + goto out; + } + } + + rsp->StructureSize = cpu_to_le16(17); + rsp->DataOffset = 80; + rsp->Reserved = 0; + rsp->DataLength = cpu_to_le32(nbytes); + rsp->DataRemaining = cpu_to_le32(remain_bytes); + rsp->Reserved2 = 0; + inc_rfc1001_len(work->response_buf, 16); + work->resp_hdr_sz = get_rfc1002_len(work->response_buf) + 4; + work->aux_payload_sz = nbytes; + inc_rfc1001_len(work->response_buf, nbytes); + ksmbd_fd_put(work, fp); + return 0; + +out: + if (err) { + if (err == -EISDIR) + rsp->hdr.Status = STATUS_INVALID_DEVICE_REQUEST; + else if (err == -EAGAIN) + rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; + else if (err == -ENOENT) + rsp->hdr.Status = STATUS_FILE_CLOSED; + else if (err == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (err == -ESHARE) + rsp->hdr.Status = STATUS_SHARING_VIOLATION; + else if (err == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else + rsp->hdr.Status = STATUS_INVALID_HANDLE; + + smb2_set_err_rsp(work); + } + ksmbd_fd_put(work, fp); + return err; +} + +/** + * smb2_write_pipe() - handler for smb2 write on IPC pipe + * @work: smb work containing write IPC pipe command buffer + * + * Return: 0 on success, otherwise error + */ +static noinline int smb2_write_pipe(struct ksmbd_work *work) +{ + struct smb2_write_req *req = smb2_get_msg(work->request_buf); + struct smb2_write_rsp *rsp = smb2_get_msg(work->response_buf); + struct ksmbd_rpc_command *rpc_resp; + u64 id = 0; + int err = 0, ret = 0; + char *data_buf; + size_t length; + + length = le32_to_cpu(req->Length); + id = req->VolatileFileId; + + if ((u64)le16_to_cpu(req->DataOffset) + length > + get_rfc1002_len(work->request_buf)) { + pr_err("invalid write data offset %u, smb_len %u\n", + le16_to_cpu(req->DataOffset), + get_rfc1002_len(work->request_buf)); + err = -EINVAL; + goto out; + } + + data_buf = (char *)(((char *)&req->hdr.ProtocolId) + + le16_to_cpu(req->DataOffset)); + + rpc_resp = ksmbd_rpc_write(work->sess, id, data_buf, length); + if (rpc_resp) { + if (rpc_resp->flags == KSMBD_RPC_ENOTIMPLEMENTED) { + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + kvfree(rpc_resp); + smb2_set_err_rsp(work); + return -EOPNOTSUPP; + } + if (rpc_resp->flags != KSMBD_RPC_OK) { + rsp->hdr.Status = STATUS_INVALID_HANDLE; + smb2_set_err_rsp(work); + kvfree(rpc_resp); + return ret; + } + kvfree(rpc_resp); + } + + rsp->StructureSize = cpu_to_le16(17); + rsp->DataOffset = 0; + rsp->Reserved = 0; + rsp->DataLength = cpu_to_le32(length); + rsp->DataRemaining = 0; + rsp->Reserved2 = 0; + inc_rfc1001_len(work->response_buf, 16); + return 0; +out: + if (err) { + rsp->hdr.Status = STATUS_INVALID_HANDLE; + smb2_set_err_rsp(work); + } + + return err; +} + +static ssize_t smb2_write_rdma_channel(struct ksmbd_work *work, + struct smb2_write_req *req, + struct ksmbd_file *fp, + loff_t offset, size_t length, bool sync) +{ + char *data_buf; + int ret; + ssize_t nbytes; + + data_buf = kvmalloc(length, GFP_KERNEL | __GFP_ZERO); + if (!data_buf) + return -ENOMEM; + + ret = ksmbd_conn_rdma_read(work->conn, data_buf, length, + (struct smb2_buffer_desc_v1 *) + ((char *)req + le16_to_cpu(req->WriteChannelInfoOffset)), + le16_to_cpu(req->WriteChannelInfoLength)); + if (ret < 0) { + kvfree(data_buf); + return ret; + } + + ret = ksmbd_vfs_write(work, fp, data_buf, length, &offset, sync, &nbytes); + kvfree(data_buf); + if (ret < 0) + return ret; + + return nbytes; +} + +/** + * smb2_write() - handler for smb2 write from file + * @work: smb work containing write command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_write(struct ksmbd_work *work) +{ + struct smb2_write_req *req; + struct smb2_write_rsp *rsp; + struct ksmbd_file *fp = NULL; + loff_t offset; + size_t length; + ssize_t nbytes; + char *data_buf; + bool writethrough = false, is_rdma_channel = false; + int err = 0; + unsigned int max_write_size = work->conn->vals->max_write_size; + + WORK_BUFFERS(work, req, rsp); + + if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_PIPE)) { + ksmbd_debug(SMB, "IPC pipe write request\n"); + return smb2_write_pipe(work); + } + + offset = le64_to_cpu(req->Offset); + length = le32_to_cpu(req->Length); + + if (req->Channel == SMB2_CHANNEL_RDMA_V1 || + req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE) { + is_rdma_channel = true; + max_write_size = get_smbd_max_read_write_size(); + length = le32_to_cpu(req->RemainingBytes); + } + + if (is_rdma_channel == true) { + unsigned int ch_offset = le16_to_cpu(req->WriteChannelInfoOffset); + + if (req->Length != 0 || req->DataOffset != 0 || + ch_offset < offsetof(struct smb2_write_req, Buffer)) { + err = -EINVAL; + goto out; + } + err = smb2_set_remote_key_for_rdma(work, + (struct smb2_buffer_desc_v1 *) + ((char *)req + ch_offset), + req->Channel, + req->WriteChannelInfoLength); + if (err) + goto out; + } + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, "User does not have write permission\n"); + err = -EACCES; + goto out; + } + + fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); + if (!fp) { + err = -ENOENT; + goto out; + } + + if (!(fp->daccess & (FILE_WRITE_DATA_LE | FILE_READ_ATTRIBUTES_LE))) { + pr_err("Not permitted to write : 0x%x\n", fp->daccess); + err = -EACCES; + goto out; + } + + if (length > max_write_size) { + ksmbd_debug(SMB, "limiting write size to max size(%u)\n", + max_write_size); + err = -EINVAL; + goto out; + } + + ksmbd_debug(SMB, "flags %u\n", le32_to_cpu(req->Flags)); + if (le32_to_cpu(req->Flags) & SMB2_WRITEFLAG_WRITE_THROUGH) + writethrough = true; + + if (is_rdma_channel == false) { + if (le16_to_cpu(req->DataOffset) < + offsetof(struct smb2_write_req, Buffer)) { + err = -EINVAL; + goto out; + } + + data_buf = (char *)(((char *)&req->hdr.ProtocolId) + + le16_to_cpu(req->DataOffset)); + + ksmbd_debug(SMB, "filename %pD, offset %lld, len %zu\n", + fp->filp, offset, length); + err = ksmbd_vfs_write(work, fp, data_buf, length, &offset, + writethrough, &nbytes); + if (err < 0) + goto out; + } else { + /* read data from the client using rdma channel, and + * write the data. + */ + nbytes = smb2_write_rdma_channel(work, req, fp, offset, length, + writethrough); + if (nbytes < 0) { + err = (int)nbytes; + goto out; + } + } + + rsp->StructureSize = cpu_to_le16(17); + rsp->DataOffset = 0; + rsp->Reserved = 0; + rsp->DataLength = cpu_to_le32(nbytes); + rsp->DataRemaining = 0; + rsp->Reserved2 = 0; + inc_rfc1001_len(work->response_buf, 16); + ksmbd_fd_put(work, fp); + return 0; + +out: + if (err == -EAGAIN) + rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; + else if (err == -ENOSPC || err == -EFBIG) + rsp->hdr.Status = STATUS_DISK_FULL; + else if (err == -ENOENT) + rsp->hdr.Status = STATUS_FILE_CLOSED; + else if (err == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (err == -ESHARE) + rsp->hdr.Status = STATUS_SHARING_VIOLATION; + else if (err == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else + rsp->hdr.Status = STATUS_INVALID_HANDLE; + + smb2_set_err_rsp(work); + ksmbd_fd_put(work, fp); + return err; +} + +/** + * smb2_flush() - handler for smb2 flush file - fsync + * @work: smb work containing flush command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_flush(struct ksmbd_work *work) +{ + struct smb2_flush_req *req; + struct smb2_flush_rsp *rsp; + int err; + + WORK_BUFFERS(work, req, rsp); + + ksmbd_debug(SMB, "SMB2_FLUSH called for fid %llu\n", req->VolatileFileId); + + err = ksmbd_vfs_fsync(work, req->VolatileFileId, req->PersistentFileId); + if (err) + goto out; + + rsp->StructureSize = cpu_to_le16(4); + rsp->Reserved = 0; + inc_rfc1001_len(work->response_buf, 4); + return 0; + +out: + if (err) { + rsp->hdr.Status = STATUS_INVALID_HANDLE; + smb2_set_err_rsp(work); + } + + return err; +} + +/** + * smb2_cancel() - handler for smb2 cancel command + * @work: smb work containing cancel command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_cancel(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_hdr *hdr = smb2_get_msg(work->request_buf); + struct smb2_hdr *chdr; + struct ksmbd_work *cancel_work = NULL; + int canceled = 0; + struct list_head *command_list; + + ksmbd_debug(SMB, "smb2 cancel called on mid %llu, async flags 0x%x\n", + hdr->MessageId, hdr->Flags); + + if (hdr->Flags & SMB2_FLAGS_ASYNC_COMMAND) { + command_list = &conn->async_requests; + + spin_lock(&conn->request_lock); + list_for_each_entry(cancel_work, command_list, + async_request_entry) { + chdr = smb2_get_msg(cancel_work->request_buf); + + if (cancel_work->async_id != + le64_to_cpu(hdr->Id.AsyncId)) + continue; + + ksmbd_debug(SMB, + "smb2 with AsyncId %llu cancelled command = 0x%x\n", + le64_to_cpu(hdr->Id.AsyncId), + le16_to_cpu(chdr->Command)); + canceled = 1; + break; + } + spin_unlock(&conn->request_lock); + } else { + command_list = &conn->requests; + + spin_lock(&conn->request_lock); + list_for_each_entry(cancel_work, command_list, request_entry) { + chdr = smb2_get_msg(cancel_work->request_buf); + + if (chdr->MessageId != hdr->MessageId || + cancel_work == work) + continue; + + ksmbd_debug(SMB, + "smb2 with mid %llu cancelled command = 0x%x\n", + le64_to_cpu(hdr->MessageId), + le16_to_cpu(chdr->Command)); + canceled = 1; + break; + } + spin_unlock(&conn->request_lock); + } + + if (canceled) { + cancel_work->state = KSMBD_WORK_CANCELLED; + if (cancel_work->cancel_fn) + cancel_work->cancel_fn(cancel_work->cancel_argv); + } + + /* For SMB2_CANCEL command itself send no response*/ + work->send_no_response = 1; + return 0; +} + +struct file_lock *smb_flock_init(struct file *f) +{ + struct file_lock *fl; + + fl = locks_alloc_lock(); + if (!fl) + goto out; + + locks_init_lock(fl); + + fl->fl_owner = f; + fl->fl_pid = current->tgid; + fl->fl_file = f; + fl->fl_flags = FL_POSIX; + fl->fl_ops = NULL; + fl->fl_lmops = NULL; + +out: + return fl; +} + +static int smb2_set_flock_flags(struct file_lock *flock, int flags) +{ + int cmd = -EINVAL; + + /* Checking for wrong flag combination during lock request*/ + switch (flags) { + case SMB2_LOCKFLAG_SHARED: + ksmbd_debug(SMB, "received shared request\n"); + cmd = F_SETLKW; + flock->fl_type = F_RDLCK; + flock->fl_flags |= FL_SLEEP; + break; + case SMB2_LOCKFLAG_EXCLUSIVE: + ksmbd_debug(SMB, "received exclusive request\n"); + cmd = F_SETLKW; + flock->fl_type = F_WRLCK; + flock->fl_flags |= FL_SLEEP; + break; + case SMB2_LOCKFLAG_SHARED | SMB2_LOCKFLAG_FAIL_IMMEDIATELY: + ksmbd_debug(SMB, + "received shared & fail immediately request\n"); + cmd = F_SETLK; + flock->fl_type = F_RDLCK; + break; + case SMB2_LOCKFLAG_EXCLUSIVE | SMB2_LOCKFLAG_FAIL_IMMEDIATELY: + ksmbd_debug(SMB, + "received exclusive & fail immediately request\n"); + cmd = F_SETLK; + flock->fl_type = F_WRLCK; + break; + case SMB2_LOCKFLAG_UNLOCK: + ksmbd_debug(SMB, "received unlock request\n"); + flock->fl_type = F_UNLCK; + cmd = F_SETLK; + break; + } + + return cmd; +} + +static struct ksmbd_lock *smb2_lock_init(struct file_lock *flock, + unsigned int cmd, int flags, + struct list_head *lock_list) +{ + struct ksmbd_lock *lock; + + lock = kzalloc(sizeof(struct ksmbd_lock), GFP_KERNEL); + if (!lock) + return NULL; + + lock->cmd = cmd; + lock->fl = flock; + lock->start = flock->fl_start; + lock->end = flock->fl_end; + lock->flags = flags; + if (lock->start == lock->end) + lock->zero_len = 1; + INIT_LIST_HEAD(&lock->clist); + INIT_LIST_HEAD(&lock->flist); + INIT_LIST_HEAD(&lock->llist); + list_add_tail(&lock->llist, lock_list); + + return lock; +} + +static void smb2_remove_blocked_lock(void **argv) +{ + struct file_lock *flock = (struct file_lock *)argv[0]; + + ksmbd_vfs_posix_lock_unblock(flock); + wake_up(&flock->fl_wait); +} + +static inline bool lock_defer_pending(struct file_lock *fl) +{ + /* check pending lock waiters */ + return waitqueue_active(&fl->fl_wait); +} + +/** + * smb2_lock() - handler for smb2 file lock command + * @work: smb work containing lock command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_lock(struct ksmbd_work *work) +{ + struct smb2_lock_req *req = smb2_get_msg(work->request_buf); + struct smb2_lock_rsp *rsp = smb2_get_msg(work->response_buf); + struct smb2_lock_element *lock_ele; + struct ksmbd_file *fp = NULL; + struct file_lock *flock = NULL; + struct file *filp = NULL; + int lock_count; + int flags = 0; + int cmd = 0; + int err = -EIO, i, rc = 0; + u64 lock_start, lock_length; + struct ksmbd_lock *smb_lock = NULL, *cmp_lock, *tmp, *tmp2; + struct ksmbd_conn *conn; + int nolock = 0; + LIST_HEAD(lock_list); + LIST_HEAD(rollback_list); + int prior_lock = 0; + + ksmbd_debug(SMB, "Received lock request\n"); + fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); + if (!fp) { + ksmbd_debug(SMB, "Invalid file id for lock : %llu\n", req->VolatileFileId); + err = -ENOENT; + goto out2; + } + + filp = fp->filp; + lock_count = le16_to_cpu(req->LockCount); + lock_ele = req->locks; + + ksmbd_debug(SMB, "lock count is %d\n", lock_count); + if (!lock_count) { + err = -EINVAL; + goto out2; + } + + for (i = 0; i < lock_count; i++) { + flags = le32_to_cpu(lock_ele[i].Flags); + + flock = smb_flock_init(filp); + if (!flock) + goto out; + + cmd = smb2_set_flock_flags(flock, flags); + + lock_start = le64_to_cpu(lock_ele[i].Offset); + lock_length = le64_to_cpu(lock_ele[i].Length); + if (lock_start > U64_MAX - lock_length) { + pr_err("Invalid lock range requested\n"); + rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE; + locks_free_lock(flock); + goto out; + } + + if (lock_start > OFFSET_MAX) + flock->fl_start = OFFSET_MAX; + else + flock->fl_start = lock_start; + + lock_length = le64_to_cpu(lock_ele[i].Length); + if (lock_length > OFFSET_MAX - flock->fl_start) + lock_length = OFFSET_MAX - flock->fl_start; + + flock->fl_end = flock->fl_start + lock_length; + + if (flock->fl_end < flock->fl_start) { + ksmbd_debug(SMB, + "the end offset(%llx) is smaller than the start offset(%llx)\n", + flock->fl_end, flock->fl_start); + rsp->hdr.Status = STATUS_INVALID_LOCK_RANGE; + locks_free_lock(flock); + goto out; + } + + /* Check conflict locks in one request */ + list_for_each_entry(cmp_lock, &lock_list, llist) { + if (cmp_lock->fl->fl_start <= flock->fl_start && + cmp_lock->fl->fl_end >= flock->fl_end) { + if (cmp_lock->fl->fl_type != F_UNLCK && + flock->fl_type != F_UNLCK) { + pr_err("conflict two locks in one request\n"); + err = -EINVAL; + locks_free_lock(flock); + goto out; + } + } + } + + smb_lock = smb2_lock_init(flock, cmd, flags, &lock_list); + if (!smb_lock) { + err = -EINVAL; + locks_free_lock(flock); + goto out; + } + } + + list_for_each_entry_safe(smb_lock, tmp, &lock_list, llist) { + if (smb_lock->cmd < 0) { + err = -EINVAL; + goto out; + } + + if (!(smb_lock->flags & SMB2_LOCKFLAG_MASK)) { + err = -EINVAL; + goto out; + } + + if ((prior_lock & (SMB2_LOCKFLAG_EXCLUSIVE | SMB2_LOCKFLAG_SHARED) && + smb_lock->flags & SMB2_LOCKFLAG_UNLOCK) || + (prior_lock == SMB2_LOCKFLAG_UNLOCK && + !(smb_lock->flags & SMB2_LOCKFLAG_UNLOCK))) { + err = -EINVAL; + goto out; + } + + prior_lock = smb_lock->flags; + + if (!(smb_lock->flags & SMB2_LOCKFLAG_UNLOCK) && + !(smb_lock->flags & SMB2_LOCKFLAG_FAIL_IMMEDIATELY)) + goto no_check_cl; + + nolock = 1; + /* check locks in connection list */ + read_lock(&conn_list_lock); + list_for_each_entry(conn, &conn_list, conns_list) { + spin_lock(&conn->llist_lock); + list_for_each_entry_safe(cmp_lock, tmp2, &conn->lock_list, clist) { + if (file_inode(cmp_lock->fl->fl_file) != + file_inode(smb_lock->fl->fl_file)) + continue; + + if (smb_lock->fl->fl_type == F_UNLCK) { + if (cmp_lock->fl->fl_file == smb_lock->fl->fl_file && + cmp_lock->start == smb_lock->start && + cmp_lock->end == smb_lock->end && + !lock_defer_pending(cmp_lock->fl)) { + nolock = 0; + list_del(&cmp_lock->flist); + list_del(&cmp_lock->clist); + spin_unlock(&conn->llist_lock); + read_unlock(&conn_list_lock); + + locks_free_lock(cmp_lock->fl); + kfree(cmp_lock); + goto out_check_cl; + } + continue; + } + + if (cmp_lock->fl->fl_file == smb_lock->fl->fl_file) { + if (smb_lock->flags & SMB2_LOCKFLAG_SHARED) + continue; + } else { + if (cmp_lock->flags & SMB2_LOCKFLAG_SHARED) + continue; + } + + /* check zero byte lock range */ + if (cmp_lock->zero_len && !smb_lock->zero_len && + cmp_lock->start > smb_lock->start && + cmp_lock->start < smb_lock->end) { + spin_unlock(&conn->llist_lock); + read_unlock(&conn_list_lock); + pr_err("previous lock conflict with zero byte lock range\n"); + goto out; + } + + if (smb_lock->zero_len && !cmp_lock->zero_len && + smb_lock->start > cmp_lock->start && + smb_lock->start < cmp_lock->end) { + spin_unlock(&conn->llist_lock); + read_unlock(&conn_list_lock); + pr_err("current lock conflict with zero byte lock range\n"); + goto out; + } + + if (((cmp_lock->start <= smb_lock->start && + cmp_lock->end > smb_lock->start) || + (cmp_lock->start < smb_lock->end && + cmp_lock->end >= smb_lock->end)) && + !cmp_lock->zero_len && !smb_lock->zero_len) { + spin_unlock(&conn->llist_lock); + read_unlock(&conn_list_lock); + pr_err("Not allow lock operation on exclusive lock range\n"); + goto out; + } + } + spin_unlock(&conn->llist_lock); + } + read_unlock(&conn_list_lock); +out_check_cl: + if (smb_lock->fl->fl_type == F_UNLCK && nolock) { + pr_err("Try to unlock nolocked range\n"); + rsp->hdr.Status = STATUS_RANGE_NOT_LOCKED; + goto out; + } + +no_check_cl: + if (smb_lock->zero_len) { + err = 0; + goto skip; + } + + flock = smb_lock->fl; + list_del(&smb_lock->llist); +retry: + rc = vfs_lock_file(filp, smb_lock->cmd, flock, NULL); +skip: + if (flags & SMB2_LOCKFLAG_UNLOCK) { + if (!rc) { + ksmbd_debug(SMB, "File unlocked\n"); + } else if (rc == -ENOENT) { + rsp->hdr.Status = STATUS_NOT_LOCKED; + goto out; + } + locks_free_lock(flock); + kfree(smb_lock); + } else { + if (rc == FILE_LOCK_DEFERRED) { + void **argv; + + ksmbd_debug(SMB, + "would have to wait for getting lock\n"); + spin_lock(&work->conn->llist_lock); + list_add_tail(&smb_lock->clist, + &work->conn->lock_list); + spin_unlock(&work->conn->llist_lock); + list_add(&smb_lock->llist, &rollback_list); + + argv = kmalloc(sizeof(void *), GFP_KERNEL); + if (!argv) { + err = -ENOMEM; + goto out; + } + argv[0] = flock; + + rc = setup_async_work(work, + smb2_remove_blocked_lock, + argv); + if (rc) { + err = -ENOMEM; + goto out; + } + spin_lock(&fp->f_lock); + list_add(&work->fp_entry, &fp->blocked_works); + spin_unlock(&fp->f_lock); + + smb2_send_interim_resp(work, STATUS_PENDING); + + ksmbd_vfs_posix_lock_wait(flock); + + if (work->state != KSMBD_WORK_ACTIVE) { + list_del(&smb_lock->llist); + spin_lock(&work->conn->llist_lock); + list_del(&smb_lock->clist); + spin_unlock(&work->conn->llist_lock); + locks_free_lock(flock); + + if (work->state == KSMBD_WORK_CANCELLED) { + spin_lock(&fp->f_lock); + list_del(&work->fp_entry); + spin_unlock(&fp->f_lock); + rsp->hdr.Status = + STATUS_CANCELLED; + kfree(smb_lock); + smb2_send_interim_resp(work, + STATUS_CANCELLED); + work->send_no_response = 1; + goto out; + } + init_smb2_rsp_hdr(work); + smb2_set_err_rsp(work); + rsp->hdr.Status = + STATUS_RANGE_NOT_LOCKED; + kfree(smb_lock); + goto out2; + } + + list_del(&smb_lock->llist); + spin_lock(&work->conn->llist_lock); + list_del(&smb_lock->clist); + spin_unlock(&work->conn->llist_lock); + + spin_lock(&fp->f_lock); + list_del(&work->fp_entry); + spin_unlock(&fp->f_lock); + goto retry; + } else if (!rc) { + spin_lock(&work->conn->llist_lock); + list_add_tail(&smb_lock->clist, + &work->conn->lock_list); + list_add_tail(&smb_lock->flist, + &fp->lock_list); + spin_unlock(&work->conn->llist_lock); + list_add(&smb_lock->llist, &rollback_list); + ksmbd_debug(SMB, "successful in taking lock\n"); + } else { + goto out; + } + } + } + + if (atomic_read(&fp->f_ci->op_count) > 1) + smb_break_all_oplock(work, fp); + + rsp->StructureSize = cpu_to_le16(4); + ksmbd_debug(SMB, "successful in taking lock\n"); + rsp->hdr.Status = STATUS_SUCCESS; + rsp->Reserved = 0; + inc_rfc1001_len(work->response_buf, 4); + ksmbd_fd_put(work, fp); + return 0; + +out: + list_for_each_entry_safe(smb_lock, tmp, &lock_list, llist) { + locks_free_lock(smb_lock->fl); + list_del(&smb_lock->llist); + kfree(smb_lock); + } + + list_for_each_entry_safe(smb_lock, tmp, &rollback_list, llist) { + struct file_lock *rlock = NULL; + + rlock = smb_flock_init(filp); + rlock->fl_type = F_UNLCK; + rlock->fl_start = smb_lock->start; + rlock->fl_end = smb_lock->end; + + rc = vfs_lock_file(filp, F_SETLK, rlock, NULL); + if (rc) + pr_err("rollback unlock fail : %d\n", rc); + + list_del(&smb_lock->llist); + spin_lock(&work->conn->llist_lock); + if (!list_empty(&smb_lock->flist)) + list_del(&smb_lock->flist); + list_del(&smb_lock->clist); + spin_unlock(&work->conn->llist_lock); + + locks_free_lock(smb_lock->fl); + locks_free_lock(rlock); + kfree(smb_lock); + } +out2: + ksmbd_debug(SMB, "failed in taking lock(flags : %x), err : %d\n", flags, err); + + if (!rsp->hdr.Status) { + if (err == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (err == -ENOMEM) + rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; + else if (err == -ENOENT) + rsp->hdr.Status = STATUS_FILE_CLOSED; + else + rsp->hdr.Status = STATUS_LOCK_NOT_GRANTED; + } + + smb2_set_err_rsp(work); + ksmbd_fd_put(work, fp); + return err; +} + +static int fsctl_copychunk(struct ksmbd_work *work, + struct copychunk_ioctl_req *ci_req, + unsigned int cnt_code, + unsigned int input_count, + unsigned long long volatile_id, + unsigned long long persistent_id, + struct smb2_ioctl_rsp *rsp) +{ + struct copychunk_ioctl_rsp *ci_rsp; + struct ksmbd_file *src_fp = NULL, *dst_fp = NULL; + struct srv_copychunk *chunks; + unsigned int i, chunk_count, chunk_count_written = 0; + unsigned int chunk_size_written = 0; + loff_t total_size_written = 0; + int ret = 0; + + ci_rsp = (struct copychunk_ioctl_rsp *)&rsp->Buffer[0]; + + rsp->VolatileFileId = volatile_id; + rsp->PersistentFileId = persistent_id; + ci_rsp->ChunksWritten = + cpu_to_le32(ksmbd_server_side_copy_max_chunk_count()); + ci_rsp->ChunkBytesWritten = + cpu_to_le32(ksmbd_server_side_copy_max_chunk_size()); + ci_rsp->TotalBytesWritten = + cpu_to_le32(ksmbd_server_side_copy_max_total_size()); + + chunks = (struct srv_copychunk *)&ci_req->Chunks[0]; + chunk_count = le32_to_cpu(ci_req->ChunkCount); + if (chunk_count == 0) + goto out; + total_size_written = 0; + + /* verify the SRV_COPYCHUNK_COPY packet */ + if (chunk_count > ksmbd_server_side_copy_max_chunk_count() || + input_count < offsetof(struct copychunk_ioctl_req, Chunks) + + chunk_count * sizeof(struct srv_copychunk)) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + return -EINVAL; + } + + for (i = 0; i < chunk_count; i++) { + if (le32_to_cpu(chunks[i].Length) == 0 || + le32_to_cpu(chunks[i].Length) > ksmbd_server_side_copy_max_chunk_size()) + break; + total_size_written += le32_to_cpu(chunks[i].Length); + } + + if (i < chunk_count || + total_size_written > ksmbd_server_side_copy_max_total_size()) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + return -EINVAL; + } + + src_fp = ksmbd_lookup_foreign_fd(work, + le64_to_cpu(ci_req->ResumeKey[0])); + dst_fp = ksmbd_lookup_fd_slow(work, volatile_id, persistent_id); + ret = -EINVAL; + if (!src_fp || + src_fp->persistent_id != le64_to_cpu(ci_req->ResumeKey[1])) { + rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; + goto out; + } + + if (!dst_fp) { + rsp->hdr.Status = STATUS_FILE_CLOSED; + goto out; + } + + /* + * FILE_READ_DATA should only be included in + * the FSCTL_COPYCHUNK case + */ + if (cnt_code == FSCTL_COPYCHUNK && + !(dst_fp->daccess & (FILE_READ_DATA_LE | FILE_GENERIC_READ_LE))) { + rsp->hdr.Status = STATUS_ACCESS_DENIED; + goto out; + } + + ret = ksmbd_vfs_copy_file_ranges(work, src_fp, dst_fp, + chunks, chunk_count, + &chunk_count_written, + &chunk_size_written, + &total_size_written); + if (ret < 0) { + if (ret == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + if (ret == -EAGAIN) + rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; + else if (ret == -EBADF) + rsp->hdr.Status = STATUS_INVALID_HANDLE; + else if (ret == -EFBIG || ret == -ENOSPC) + rsp->hdr.Status = STATUS_DISK_FULL; + else if (ret == -EINVAL) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + else if (ret == -EISDIR) + rsp->hdr.Status = STATUS_FILE_IS_A_DIRECTORY; + else if (ret == -E2BIG) + rsp->hdr.Status = STATUS_INVALID_VIEW_SIZE; + else + rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR; + } + + ci_rsp->ChunksWritten = cpu_to_le32(chunk_count_written); + ci_rsp->ChunkBytesWritten = cpu_to_le32(chunk_size_written); + ci_rsp->TotalBytesWritten = cpu_to_le32(total_size_written); +out: + ksmbd_fd_put(work, src_fp); + ksmbd_fd_put(work, dst_fp); + return ret; +} + +static __be32 idev_ipv4_address(struct in_device *idev) +{ + __be32 addr = 0; + + struct in_ifaddr *ifa; + + rcu_read_lock(); + in_dev_for_each_ifa_rcu(ifa, idev) { + if (ifa->ifa_flags & IFA_F_SECONDARY) + continue; + + addr = ifa->ifa_address; + break; + } + rcu_read_unlock(); + return addr; +} + +static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn, + struct smb2_ioctl_rsp *rsp, + unsigned int out_buf_len) +{ + struct network_interface_info_ioctl_rsp *nii_rsp = NULL; + int nbytes = 0; + struct net_device *netdev; + struct sockaddr_storage_rsp *sockaddr_storage; + unsigned int flags; + unsigned long long speed; + + rtnl_lock(); + for_each_netdev(&init_net, netdev) { + bool ipv4_set = false; + + if (netdev->type == ARPHRD_LOOPBACK) + continue; + + flags = dev_get_flags(netdev); + if (!(flags & IFF_RUNNING)) + continue; +ipv6_retry: + if (out_buf_len < + nbytes + sizeof(struct network_interface_info_ioctl_rsp)) { + rtnl_unlock(); + return -ENOSPC; + } + + nii_rsp = (struct network_interface_info_ioctl_rsp *) + &rsp->Buffer[nbytes]; + nii_rsp->IfIndex = cpu_to_le32(netdev->ifindex); + + nii_rsp->Capability = 0; + if (netdev->real_num_tx_queues > 1) + nii_rsp->Capability |= cpu_to_le32(RSS_CAPABLE); + if (ksmbd_rdma_capable_netdev(netdev)) + nii_rsp->Capability |= cpu_to_le32(RDMA_CAPABLE); + + nii_rsp->Next = cpu_to_le32(152); + nii_rsp->Reserved = 0; + + if (netdev->ethtool_ops->get_link_ksettings) { + struct ethtool_link_ksettings cmd; + + netdev->ethtool_ops->get_link_ksettings(netdev, &cmd); + speed = cmd.base.speed; + } else { + ksmbd_debug(SMB, "%s %s\n", netdev->name, + "speed is unknown, defaulting to 1Gb/sec"); + speed = SPEED_1000; + } + + speed *= 1000000; + nii_rsp->LinkSpeed = cpu_to_le64(speed); + + sockaddr_storage = (struct sockaddr_storage_rsp *) + nii_rsp->SockAddr_Storage; + memset(sockaddr_storage, 0, 128); + + if (!ipv4_set) { + struct in_device *idev; + + sockaddr_storage->Family = cpu_to_le16(INTERNETWORK); + sockaddr_storage->addr4.Port = 0; + + idev = __in_dev_get_rtnl(netdev); + if (!idev) + continue; + sockaddr_storage->addr4.IPv4address = + idev_ipv4_address(idev); + nbytes += sizeof(struct network_interface_info_ioctl_rsp); + ipv4_set = true; + goto ipv6_retry; + } else { + struct inet6_dev *idev6; + struct inet6_ifaddr *ifa; + __u8 *ipv6_addr = sockaddr_storage->addr6.IPv6address; + + sockaddr_storage->Family = cpu_to_le16(INTERNETWORKV6); + sockaddr_storage->addr6.Port = 0; + sockaddr_storage->addr6.FlowInfo = 0; + + idev6 = __in6_dev_get(netdev); + if (!idev6) + continue; + + list_for_each_entry(ifa, &idev6->addr_list, if_list) { + if (ifa->flags & (IFA_F_TENTATIVE | + IFA_F_DEPRECATED)) + continue; + memcpy(ipv6_addr, ifa->addr.s6_addr, 16); + break; + } + sockaddr_storage->addr6.ScopeId = 0; + nbytes += sizeof(struct network_interface_info_ioctl_rsp); + } + } + rtnl_unlock(); + + /* zero if this is last one */ + if (nii_rsp) + nii_rsp->Next = 0; + + rsp->PersistentFileId = SMB2_NO_FID; + rsp->VolatileFileId = SMB2_NO_FID; + return nbytes; +} + +static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn, + struct validate_negotiate_info_req *neg_req, + struct validate_negotiate_info_rsp *neg_rsp, + unsigned int in_buf_len) +{ + int ret = 0; + int dialect; + + if (in_buf_len < offsetof(struct validate_negotiate_info_req, Dialects) + + le16_to_cpu(neg_req->DialectCount) * sizeof(__le16)) + return -EINVAL; + + dialect = ksmbd_lookup_dialect_by_id(neg_req->Dialects, + neg_req->DialectCount); + if (dialect == BAD_PROT_ID || dialect != conn->dialect) { + ret = -EINVAL; + goto err_out; + } + + if (strncmp(neg_req->Guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE)) { + ret = -EINVAL; + goto err_out; + } + + if (le16_to_cpu(neg_req->SecurityMode) != conn->cli_sec_mode) { + ret = -EINVAL; + goto err_out; + } + + if (le32_to_cpu(neg_req->Capabilities) != conn->cli_cap) { + ret = -EINVAL; + goto err_out; + } + + neg_rsp->Capabilities = cpu_to_le32(conn->vals->capabilities); + memset(neg_rsp->Guid, 0, SMB2_CLIENT_GUID_SIZE); + neg_rsp->SecurityMode = cpu_to_le16(conn->srv_sec_mode); + neg_rsp->Dialect = cpu_to_le16(conn->dialect); +err_out: + return ret; +} + +static int fsctl_query_allocated_ranges(struct ksmbd_work *work, u64 id, + struct file_allocated_range_buffer *qar_req, + struct file_allocated_range_buffer *qar_rsp, + unsigned int in_count, unsigned int *out_count) +{ + struct ksmbd_file *fp; + loff_t start, length; + int ret = 0; + + *out_count = 0; + if (in_count == 0) + return -EINVAL; + + fp = ksmbd_lookup_fd_fast(work, id); + if (!fp) + return -ENOENT; + + start = le64_to_cpu(qar_req->file_offset); + length = le64_to_cpu(qar_req->length); + + ret = ksmbd_vfs_fqar_lseek(fp, start, length, + qar_rsp, in_count, out_count); + if (ret && ret != -E2BIG) + *out_count = 0; + + ksmbd_fd_put(work, fp); + return ret; +} + +static int fsctl_pipe_transceive(struct ksmbd_work *work, u64 id, + unsigned int out_buf_len, + struct smb2_ioctl_req *req, + struct smb2_ioctl_rsp *rsp) +{ + struct ksmbd_rpc_command *rpc_resp; + char *data_buf = (char *)&req->Buffer[0]; + int nbytes = 0; + + rpc_resp = ksmbd_rpc_ioctl(work->sess, id, data_buf, + le32_to_cpu(req->InputCount)); + if (rpc_resp) { + if (rpc_resp->flags == KSMBD_RPC_SOME_NOT_MAPPED) { + /* + * set STATUS_SOME_NOT_MAPPED response + * for unknown domain sid. + */ + rsp->hdr.Status = STATUS_SOME_NOT_MAPPED; + } else if (rpc_resp->flags == KSMBD_RPC_ENOTIMPLEMENTED) { + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + goto out; + } else if (rpc_resp->flags != KSMBD_RPC_OK) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + goto out; + } + + nbytes = rpc_resp->payload_sz; + if (rpc_resp->payload_sz > out_buf_len) { + rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; + nbytes = out_buf_len; + } + + if (!rpc_resp->payload_sz) { + rsp->hdr.Status = + STATUS_UNEXPECTED_IO_ERROR; + goto out; + } + + memcpy((char *)rsp->Buffer, rpc_resp->payload, nbytes); + } +out: + kvfree(rpc_resp); + return nbytes; +} + +static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id, + struct file_sparse *sparse) +{ + struct ksmbd_file *fp; + struct user_namespace *user_ns; + int ret = 0; + __le32 old_fattr; + + fp = ksmbd_lookup_fd_fast(work, id); + if (!fp) + return -ENOENT; + user_ns = file_mnt_user_ns(fp->filp); + + old_fattr = fp->f_ci->m_fattr; + if (sparse->SetSparse) + fp->f_ci->m_fattr |= ATTR_SPARSE_FILE_LE; + else + fp->f_ci->m_fattr &= ~ATTR_SPARSE_FILE_LE; + + if (fp->f_ci->m_fattr != old_fattr && + test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + struct xattr_dos_attrib da; + + ret = ksmbd_vfs_get_dos_attrib_xattr(user_ns, + fp->filp->f_path.dentry, &da); + if (ret <= 0) + goto out; + + da.attr = le32_to_cpu(fp->f_ci->m_fattr); + ret = ksmbd_vfs_set_dos_attrib_xattr(user_ns, + fp->filp->f_path.dentry, &da); + if (ret) + fp->f_ci->m_fattr = old_fattr; + } + +out: + ksmbd_fd_put(work, fp); + return ret; +} + +static int fsctl_request_resume_key(struct ksmbd_work *work, + struct smb2_ioctl_req *req, + struct resume_key_ioctl_rsp *key_rsp) +{ + struct ksmbd_file *fp; + + fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); + if (!fp) + return -ENOENT; + + memset(key_rsp, 0, sizeof(*key_rsp)); + key_rsp->ResumeKey[0] = req->VolatileFileId; + key_rsp->ResumeKey[1] = req->PersistentFileId; + ksmbd_fd_put(work, fp); + + return 0; +} + +/** + * smb2_ioctl() - handler for smb2 ioctl command + * @work: smb work containing ioctl command buffer + * + * Return: 0 on success, otherwise error + */ +int smb2_ioctl(struct ksmbd_work *work) +{ + struct smb2_ioctl_req *req; + struct smb2_ioctl_rsp *rsp; + unsigned int cnt_code, nbytes = 0, out_buf_len, in_buf_len; + u64 id = KSMBD_NO_FID; + struct ksmbd_conn *conn = work->conn; + int ret = 0; + + if (work->next_smb2_rcv_hdr_off) { + req = ksmbd_req_buf_next(work); + rsp = ksmbd_resp_buf_next(work); + if (!has_file_id(req->VolatileFileId)) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", + work->compound_fid); + id = work->compound_fid; + } + } else { + req = smb2_get_msg(work->request_buf); + rsp = smb2_get_msg(work->response_buf); + } + + if (!has_file_id(id)) + id = req->VolatileFileId; + + if (req->Flags != cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL)) { + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + goto out; + } + + cnt_code = le32_to_cpu(req->CntCode); + ret = smb2_calc_max_out_buf_len(work, 48, + le32_to_cpu(req->MaxOutputResponse)); + if (ret < 0) { + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + goto out; + } + out_buf_len = (unsigned int)ret; + in_buf_len = le32_to_cpu(req->InputCount); + + switch (cnt_code) { + case FSCTL_DFS_GET_REFERRALS: + case FSCTL_DFS_GET_REFERRALS_EX: + /* Not support DFS yet */ + rsp->hdr.Status = STATUS_FS_DRIVER_REQUIRED; + goto out; + case FSCTL_CREATE_OR_GET_OBJECT_ID: + { + struct file_object_buf_type1_ioctl_rsp *obj_buf; + + nbytes = sizeof(struct file_object_buf_type1_ioctl_rsp); + obj_buf = (struct file_object_buf_type1_ioctl_rsp *) + &rsp->Buffer[0]; + + /* + * TODO: This is dummy implementation to pass smbtorture + * Need to check correct response later + */ + memset(obj_buf->ObjectId, 0x0, 16); + memset(obj_buf->BirthVolumeId, 0x0, 16); + memset(obj_buf->BirthObjectId, 0x0, 16); + memset(obj_buf->DomainId, 0x0, 16); + + break; + } + case FSCTL_PIPE_TRANSCEIVE: + out_buf_len = min_t(u32, KSMBD_IPC_MAX_PAYLOAD, out_buf_len); + nbytes = fsctl_pipe_transceive(work, id, out_buf_len, req, rsp); + break; + case FSCTL_VALIDATE_NEGOTIATE_INFO: + if (conn->dialect < SMB30_PROT_ID) { + ret = -EOPNOTSUPP; + goto out; + } + + if (in_buf_len < offsetof(struct validate_negotiate_info_req, + Dialects)) { + ret = -EINVAL; + goto out; + } + + if (out_buf_len < sizeof(struct validate_negotiate_info_rsp)) { + ret = -EINVAL; + goto out; + } + + ret = fsctl_validate_negotiate_info(conn, + (struct validate_negotiate_info_req *)&req->Buffer[0], + (struct validate_negotiate_info_rsp *)&rsp->Buffer[0], + in_buf_len); + if (ret < 0) + goto out; + + nbytes = sizeof(struct validate_negotiate_info_rsp); + rsp->PersistentFileId = SMB2_NO_FID; + rsp->VolatileFileId = SMB2_NO_FID; + break; + case FSCTL_QUERY_NETWORK_INTERFACE_INFO: + ret = fsctl_query_iface_info_ioctl(conn, rsp, out_buf_len); + if (ret < 0) + goto out; + nbytes = ret; + break; + case FSCTL_REQUEST_RESUME_KEY: + if (out_buf_len < sizeof(struct resume_key_ioctl_rsp)) { + ret = -EINVAL; + goto out; + } + + ret = fsctl_request_resume_key(work, req, + (struct resume_key_ioctl_rsp *)&rsp->Buffer[0]); + if (ret < 0) + goto out; + rsp->PersistentFileId = req->PersistentFileId; + rsp->VolatileFileId = req->VolatileFileId; + nbytes = sizeof(struct resume_key_ioctl_rsp); + break; + case FSCTL_COPYCHUNK: + case FSCTL_COPYCHUNK_WRITE: + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + ret = -EACCES; + goto out; + } + + if (in_buf_len < sizeof(struct copychunk_ioctl_req)) { + ret = -EINVAL; + goto out; + } + + if (out_buf_len < sizeof(struct copychunk_ioctl_rsp)) { + ret = -EINVAL; + goto out; + } + + nbytes = sizeof(struct copychunk_ioctl_rsp); + rsp->VolatileFileId = req->VolatileFileId; + rsp->PersistentFileId = req->PersistentFileId; + fsctl_copychunk(work, + (struct copychunk_ioctl_req *)&req->Buffer[0], + le32_to_cpu(req->CntCode), + le32_to_cpu(req->InputCount), + req->VolatileFileId, + req->PersistentFileId, + rsp); + break; + case FSCTL_SET_SPARSE: + if (in_buf_len < sizeof(struct file_sparse)) { + ret = -EINVAL; + goto out; + } + + ret = fsctl_set_sparse(work, id, + (struct file_sparse *)&req->Buffer[0]); + if (ret < 0) + goto out; + break; + case FSCTL_SET_ZERO_DATA: + { + struct file_zero_data_information *zero_data; + struct ksmbd_file *fp; + loff_t off, len, bfz; + + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, + "User does not have write permission\n"); + ret = -EACCES; + goto out; + } + + if (in_buf_len < sizeof(struct file_zero_data_information)) { + ret = -EINVAL; + goto out; + } + + zero_data = + (struct file_zero_data_information *)&req->Buffer[0]; + + off = le64_to_cpu(zero_data->FileOffset); + bfz = le64_to_cpu(zero_data->BeyondFinalZero); + if (off > bfz) { + ret = -EINVAL; + goto out; + } + + len = bfz - off; + if (len) { + fp = ksmbd_lookup_fd_fast(work, id); + if (!fp) { + ret = -ENOENT; + goto out; + } + + ret = ksmbd_vfs_zero_data(work, fp, off, len); + ksmbd_fd_put(work, fp); + if (ret < 0) + goto out; + } + break; + } + case FSCTL_QUERY_ALLOCATED_RANGES: + if (in_buf_len < sizeof(struct file_allocated_range_buffer)) { + ret = -EINVAL; + goto out; + } + + ret = fsctl_query_allocated_ranges(work, id, + (struct file_allocated_range_buffer *)&req->Buffer[0], + (struct file_allocated_range_buffer *)&rsp->Buffer[0], + out_buf_len / + sizeof(struct file_allocated_range_buffer), &nbytes); + if (ret == -E2BIG) { + rsp->hdr.Status = STATUS_BUFFER_OVERFLOW; + } else if (ret < 0) { + nbytes = 0; + goto out; + } + + nbytes *= sizeof(struct file_allocated_range_buffer); + break; + case FSCTL_GET_REPARSE_POINT: + { + struct reparse_data_buffer *reparse_ptr; + struct ksmbd_file *fp; + + reparse_ptr = (struct reparse_data_buffer *)&rsp->Buffer[0]; + fp = ksmbd_lookup_fd_fast(work, id); + if (!fp) { + pr_err("not found fp!!\n"); + ret = -ENOENT; + goto out; + } + + reparse_ptr->ReparseTag = + smb2_get_reparse_tag_special_file(file_inode(fp->filp)->i_mode); + reparse_ptr->ReparseDataLength = 0; + ksmbd_fd_put(work, fp); + nbytes = sizeof(struct reparse_data_buffer); + break; + } + case FSCTL_DUPLICATE_EXTENTS_TO_FILE: + { + struct ksmbd_file *fp_in, *fp_out = NULL; + struct duplicate_extents_to_file *dup_ext; + loff_t src_off, dst_off, length, cloned; + + if (in_buf_len < sizeof(struct duplicate_extents_to_file)) { + ret = -EINVAL; + goto out; + } + + dup_ext = (struct duplicate_extents_to_file *)&req->Buffer[0]; + + fp_in = ksmbd_lookup_fd_slow(work, dup_ext->VolatileFileHandle, + dup_ext->PersistentFileHandle); + if (!fp_in) { + pr_err("not found file handle in duplicate extent to file\n"); + ret = -ENOENT; + goto out; + } + + fp_out = ksmbd_lookup_fd_fast(work, id); + if (!fp_out) { + pr_err("not found fp\n"); + ret = -ENOENT; + goto dup_ext_out; + } + + src_off = le64_to_cpu(dup_ext->SourceFileOffset); + dst_off = le64_to_cpu(dup_ext->TargetFileOffset); + length = le64_to_cpu(dup_ext->ByteCount); + /* + * XXX: It is not clear if FSCTL_DUPLICATE_EXTENTS_TO_FILE + * should fall back to vfs_copy_file_range(). This could be + * beneficial when re-exporting nfs/smb mount, but note that + * this can result in partial copy that returns an error status. + * If/when FSCTL_DUPLICATE_EXTENTS_TO_FILE_EX is implemented, + * fall back to vfs_copy_file_range(), should be avoided when + * the flag DUPLICATE_EXTENTS_DATA_EX_SOURCE_ATOMIC is set. + */ + cloned = vfs_clone_file_range(fp_in->filp, src_off, + fp_out->filp, dst_off, length, 0); + if (cloned == -EXDEV || cloned == -EOPNOTSUPP) { + ret = -EOPNOTSUPP; + goto dup_ext_out; + } else if (cloned != length) { + cloned = vfs_copy_file_range(fp_in->filp, src_off, + fp_out->filp, dst_off, + length, 0); + if (cloned != length) { + if (cloned < 0) + ret = cloned; + else + ret = -EINVAL; + } + } + +dup_ext_out: + ksmbd_fd_put(work, fp_in); + ksmbd_fd_put(work, fp_out); + if (ret < 0) + goto out; + break; + } + default: + ksmbd_debug(SMB, "not implemented yet ioctl command 0x%x\n", + cnt_code); + ret = -EOPNOTSUPP; + goto out; + } + + rsp->CntCode = cpu_to_le32(cnt_code); + rsp->InputCount = cpu_to_le32(0); + rsp->InputOffset = cpu_to_le32(112); + rsp->OutputOffset = cpu_to_le32(112); + rsp->OutputCount = cpu_to_le32(nbytes); + rsp->StructureSize = cpu_to_le16(49); + rsp->Reserved = cpu_to_le16(0); + rsp->Flags = cpu_to_le32(0); + rsp->Reserved2 = cpu_to_le32(0); + inc_rfc1001_len(work->response_buf, 48 + nbytes); + + return 0; + +out: + if (ret == -EACCES) + rsp->hdr.Status = STATUS_ACCESS_DENIED; + else if (ret == -ENOENT) + rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND; + else if (ret == -EOPNOTSUPP) + rsp->hdr.Status = STATUS_NOT_SUPPORTED; + else if (ret == -ENOSPC) + rsp->hdr.Status = STATUS_BUFFER_TOO_SMALL; + else if (ret < 0 || rsp->hdr.Status == 0) + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + smb2_set_err_rsp(work); + return 0; +} + +/** + * smb20_oplock_break_ack() - handler for smb2.0 oplock break command + * @work: smb work containing oplock break command buffer + * + * Return: 0 + */ +static void smb20_oplock_break_ack(struct ksmbd_work *work) +{ + struct smb2_oplock_break *req = smb2_get_msg(work->request_buf); + struct smb2_oplock_break *rsp = smb2_get_msg(work->response_buf); + struct ksmbd_file *fp; + struct oplock_info *opinfo = NULL; + __le32 err = 0; + int ret = 0; + u64 volatile_id, persistent_id; + char req_oplevel = 0, rsp_oplevel = 0; + unsigned int oplock_change_type; + + volatile_id = le64_to_cpu(req->VolatileFid); + persistent_id = le64_to_cpu(req->PersistentFid); + req_oplevel = req->OplockLevel; + ksmbd_debug(OPLOCK, "v_id %llu, p_id %llu request oplock level %d\n", + volatile_id, persistent_id, req_oplevel); + + fp = ksmbd_lookup_fd_slow(work, volatile_id, persistent_id); + if (!fp) { + rsp->hdr.Status = STATUS_FILE_CLOSED; + smb2_set_err_rsp(work); + return; + } + + opinfo = opinfo_get(fp); + if (!opinfo) { + pr_err("unexpected null oplock_info\n"); + rsp->hdr.Status = STATUS_INVALID_OPLOCK_PROTOCOL; + smb2_set_err_rsp(work); + ksmbd_fd_put(work, fp); + return; + } + + if (opinfo->level == SMB2_OPLOCK_LEVEL_NONE) { + rsp->hdr.Status = STATUS_INVALID_OPLOCK_PROTOCOL; + goto err_out; + } + + if (opinfo->op_state == OPLOCK_STATE_NONE) { + ksmbd_debug(SMB, "unexpected oplock state 0x%x\n", opinfo->op_state); + rsp->hdr.Status = STATUS_UNSUCCESSFUL; + goto err_out; + } + + if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || + opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && + (req_oplevel != SMB2_OPLOCK_LEVEL_II && + req_oplevel != SMB2_OPLOCK_LEVEL_NONE)) { + err = STATUS_INVALID_OPLOCK_PROTOCOL; + oplock_change_type = OPLOCK_WRITE_TO_NONE; + } else if (opinfo->level == SMB2_OPLOCK_LEVEL_II && + req_oplevel != SMB2_OPLOCK_LEVEL_NONE) { + err = STATUS_INVALID_OPLOCK_PROTOCOL; + oplock_change_type = OPLOCK_READ_TO_NONE; + } else if (req_oplevel == SMB2_OPLOCK_LEVEL_II || + req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { + err = STATUS_INVALID_DEVICE_STATE; + if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || + opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && + req_oplevel == SMB2_OPLOCK_LEVEL_II) { + oplock_change_type = OPLOCK_WRITE_TO_READ; + } else if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || + opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && + req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { + oplock_change_type = OPLOCK_WRITE_TO_NONE; + } else if (opinfo->level == SMB2_OPLOCK_LEVEL_II && + req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { + oplock_change_type = OPLOCK_READ_TO_NONE; + } else { + oplock_change_type = 0; + } + } else { + oplock_change_type = 0; + } + + switch (oplock_change_type) { + case OPLOCK_WRITE_TO_READ: + ret = opinfo_write_to_read(opinfo); + rsp_oplevel = SMB2_OPLOCK_LEVEL_II; + break; + case OPLOCK_WRITE_TO_NONE: + ret = opinfo_write_to_none(opinfo); + rsp_oplevel = SMB2_OPLOCK_LEVEL_NONE; + break; + case OPLOCK_READ_TO_NONE: + ret = opinfo_read_to_none(opinfo); + rsp_oplevel = SMB2_OPLOCK_LEVEL_NONE; + break; + default: + pr_err("unknown oplock change 0x%x -> 0x%x\n", + opinfo->level, rsp_oplevel); + } + + if (ret < 0) { + rsp->hdr.Status = err; + goto err_out; + } + + opinfo_put(opinfo); + ksmbd_fd_put(work, fp); + opinfo->op_state = OPLOCK_STATE_NONE; + wake_up_interruptible_all(&opinfo->oplock_q); + + rsp->StructureSize = cpu_to_le16(24); + rsp->OplockLevel = rsp_oplevel; + rsp->Reserved = 0; + rsp->Reserved2 = 0; + rsp->VolatileFid = cpu_to_le64(volatile_id); + rsp->PersistentFid = cpu_to_le64(persistent_id); + inc_rfc1001_len(work->response_buf, 24); + return; + +err_out: + opinfo->op_state = OPLOCK_STATE_NONE; + wake_up_interruptible_all(&opinfo->oplock_q); + + opinfo_put(opinfo); + ksmbd_fd_put(work, fp); + smb2_set_err_rsp(work); +} + +static int check_lease_state(struct lease *lease, __le32 req_state) +{ + if ((lease->new_state == + (SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE)) && + !(req_state & SMB2_LEASE_WRITE_CACHING_LE)) { + lease->new_state = req_state; + return 0; + } + + if (lease->new_state == req_state) + return 0; + + return 1; +} + +/** + * smb21_lease_break_ack() - handler for smb2.1 lease break command + * @work: smb work containing lease break command buffer + * + * Return: 0 + */ +static void smb21_lease_break_ack(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_lease_ack *req = smb2_get_msg(work->request_buf); + struct smb2_lease_ack *rsp = smb2_get_msg(work->response_buf); + struct oplock_info *opinfo; + __le32 err = 0; + int ret = 0; + unsigned int lease_change_type; + __le32 lease_state; + struct lease *lease; + + ksmbd_debug(OPLOCK, "smb21 lease break, lease state(0x%x)\n", + le32_to_cpu(req->LeaseState)); + opinfo = lookup_lease_in_table(conn, req->LeaseKey); + if (!opinfo) { + ksmbd_debug(OPLOCK, "file not opened\n"); + smb2_set_err_rsp(work); + rsp->hdr.Status = STATUS_UNSUCCESSFUL; + return; + } + lease = opinfo->o_lease; + + if (opinfo->op_state == OPLOCK_STATE_NONE) { + pr_err("unexpected lease break state 0x%x\n", + opinfo->op_state); + rsp->hdr.Status = STATUS_UNSUCCESSFUL; + goto err_out; + } + + if (check_lease_state(lease, req->LeaseState)) { + rsp->hdr.Status = STATUS_REQUEST_NOT_ACCEPTED; + ksmbd_debug(OPLOCK, + "req lease state: 0x%x, expected state: 0x%x\n", + req->LeaseState, lease->new_state); + goto err_out; + } + + if (!atomic_read(&opinfo->breaking_cnt)) { + rsp->hdr.Status = STATUS_UNSUCCESSFUL; + goto err_out; + } + + /* check for bad lease state */ + if (req->LeaseState & + (~(SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE))) { + err = STATUS_INVALID_OPLOCK_PROTOCOL; + if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) + lease_change_type = OPLOCK_WRITE_TO_NONE; + else + lease_change_type = OPLOCK_READ_TO_NONE; + ksmbd_debug(OPLOCK, "handle bad lease state 0x%x -> 0x%x\n", + le32_to_cpu(lease->state), + le32_to_cpu(req->LeaseState)); + } else if (lease->state == SMB2_LEASE_READ_CACHING_LE && + req->LeaseState != SMB2_LEASE_NONE_LE) { + err = STATUS_INVALID_OPLOCK_PROTOCOL; + lease_change_type = OPLOCK_READ_TO_NONE; + ksmbd_debug(OPLOCK, "handle bad lease state 0x%x -> 0x%x\n", + le32_to_cpu(lease->state), + le32_to_cpu(req->LeaseState)); + } else { + /* valid lease state changes */ + err = STATUS_INVALID_DEVICE_STATE; + if (req->LeaseState == SMB2_LEASE_NONE_LE) { + if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) + lease_change_type = OPLOCK_WRITE_TO_NONE; + else + lease_change_type = OPLOCK_READ_TO_NONE; + } else if (req->LeaseState & SMB2_LEASE_READ_CACHING_LE) { + if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) + lease_change_type = OPLOCK_WRITE_TO_READ; + else + lease_change_type = OPLOCK_READ_HANDLE_TO_READ; + } else { + lease_change_type = 0; + } + } + + switch (lease_change_type) { + case OPLOCK_WRITE_TO_READ: + ret = opinfo_write_to_read(opinfo); + break; + case OPLOCK_READ_HANDLE_TO_READ: + ret = opinfo_read_handle_to_read(opinfo); + break; + case OPLOCK_WRITE_TO_NONE: + ret = opinfo_write_to_none(opinfo); + break; + case OPLOCK_READ_TO_NONE: + ret = opinfo_read_to_none(opinfo); + break; + default: + ksmbd_debug(OPLOCK, "unknown lease change 0x%x -> 0x%x\n", + le32_to_cpu(lease->state), + le32_to_cpu(req->LeaseState)); + } + + lease_state = lease->state; + opinfo->op_state = OPLOCK_STATE_NONE; + wake_up_interruptible_all(&opinfo->oplock_q); + atomic_dec(&opinfo->breaking_cnt); + wake_up_interruptible_all(&opinfo->oplock_brk); + opinfo_put(opinfo); + + if (ret < 0) { + rsp->hdr.Status = err; + goto err_out; + } + + rsp->StructureSize = cpu_to_le16(36); + rsp->Reserved = 0; + rsp->Flags = 0; + memcpy(rsp->LeaseKey, req->LeaseKey, 16); + rsp->LeaseState = lease_state; + rsp->LeaseDuration = 0; + inc_rfc1001_len(work->response_buf, 36); + return; + +err_out: + opinfo->op_state = OPLOCK_STATE_NONE; + wake_up_interruptible_all(&opinfo->oplock_q); + atomic_dec(&opinfo->breaking_cnt); + wake_up_interruptible_all(&opinfo->oplock_brk); + + opinfo_put(opinfo); + smb2_set_err_rsp(work); +} + +/** + * smb2_oplock_break() - dispatcher for smb2.0 and 2.1 oplock/lease break + * @work: smb work containing oplock/lease break command buffer + * + * Return: 0 + */ +int smb2_oplock_break(struct ksmbd_work *work) +{ + struct smb2_oplock_break *req = smb2_get_msg(work->request_buf); + struct smb2_oplock_break *rsp = smb2_get_msg(work->response_buf); + + switch (le16_to_cpu(req->StructureSize)) { + case OP_BREAK_STRUCT_SIZE_20: + smb20_oplock_break_ack(work); + break; + case OP_BREAK_STRUCT_SIZE_21: + smb21_lease_break_ack(work); + break; + default: + ksmbd_debug(OPLOCK, "invalid break cmd %d\n", + le16_to_cpu(req->StructureSize)); + rsp->hdr.Status = STATUS_INVALID_PARAMETER; + smb2_set_err_rsp(work); + } + + return 0; +} + +/** + * smb2_notify() - handler for smb2 notify request + * @work: smb work containing notify command buffer + * + * Return: 0 + */ +int smb2_notify(struct ksmbd_work *work) +{ + struct smb2_notify_req *req; + struct smb2_notify_rsp *rsp; + + WORK_BUFFERS(work, req, rsp); + + if (work->next_smb2_rcv_hdr_off && req->hdr.NextCommand) { + rsp->hdr.Status = STATUS_INTERNAL_ERROR; + smb2_set_err_rsp(work); + return 0; + } + + smb2_set_err_rsp(work); + rsp->hdr.Status = STATUS_NOT_IMPLEMENTED; + return 0; +} + +/** + * smb2_is_sign_req() - handler for checking packet signing status + * @work: smb work containing notify command buffer + * @command: SMB2 command id + * + * Return: true if packed is signed, false otherwise + */ +bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command) +{ + struct smb2_hdr *rcv_hdr2 = smb2_get_msg(work->request_buf); + + if ((rcv_hdr2->Flags & SMB2_FLAGS_SIGNED) && + command != SMB2_NEGOTIATE_HE && + command != SMB2_SESSION_SETUP_HE && + command != SMB2_OPLOCK_BREAK_HE) + return true; + + return false; +} + +/** + * smb2_check_sign_req() - handler for req packet sign processing + * @work: smb work containing notify command buffer + * + * Return: 1 on success, 0 otherwise + */ +int smb2_check_sign_req(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr; + char signature_req[SMB2_SIGNATURE_SIZE]; + char signature[SMB2_HMACSHA256_SIZE]; + struct kvec iov[1]; + size_t len; + + hdr = smb2_get_msg(work->request_buf); + if (work->next_smb2_rcv_hdr_off) + hdr = ksmbd_req_buf_next(work); + + if (!hdr->NextCommand && !work->next_smb2_rcv_hdr_off) + len = get_rfc1002_len(work->request_buf); + else if (hdr->NextCommand) + len = le32_to_cpu(hdr->NextCommand); + else + len = get_rfc1002_len(work->request_buf) - + work->next_smb2_rcv_hdr_off; + + memcpy(signature_req, hdr->Signature, SMB2_SIGNATURE_SIZE); + memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); + + iov[0].iov_base = (char *)&hdr->ProtocolId; + iov[0].iov_len = len; + + if (ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, 1, + signature)) + return 0; + + if (memcmp(signature, signature_req, SMB2_SIGNATURE_SIZE)) { + pr_err("bad smb2 signature\n"); + return 0; + } + + return 1; +} + +/** + * smb2_set_sign_rsp() - handler for rsp packet sign processing + * @work: smb work containing notify command buffer + * + */ +void smb2_set_sign_rsp(struct ksmbd_work *work) +{ + struct smb2_hdr *hdr; + struct smb2_hdr *req_hdr; + char signature[SMB2_HMACSHA256_SIZE]; + struct kvec iov[2]; + size_t len; + int n_vec = 1; + + hdr = smb2_get_msg(work->response_buf); + if (work->next_smb2_rsp_hdr_off) + hdr = ksmbd_resp_buf_next(work); + + req_hdr = ksmbd_req_buf_next(work); + + if (!work->next_smb2_rsp_hdr_off) { + len = get_rfc1002_len(work->response_buf); + if (req_hdr->NextCommand) + len = ALIGN(len, 8); + } else { + len = get_rfc1002_len(work->response_buf) - + work->next_smb2_rsp_hdr_off; + len = ALIGN(len, 8); + } + + if (req_hdr->NextCommand) + hdr->NextCommand = cpu_to_le32(len); + + hdr->Flags |= SMB2_FLAGS_SIGNED; + memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); + + iov[0].iov_base = (char *)&hdr->ProtocolId; + iov[0].iov_len = len; + + if (work->aux_payload_sz) { + iov[0].iov_len -= work->aux_payload_sz; + + iov[1].iov_base = work->aux_payload_buf; + iov[1].iov_len = work->aux_payload_sz; + n_vec++; + } + + if (!ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, n_vec, + signature)) + memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE); +} + +/** + * smb3_check_sign_req() - handler for req packet sign processing + * @work: smb work containing notify command buffer + * + * Return: 1 on success, 0 otherwise + */ +int smb3_check_sign_req(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + char *signing_key; + struct smb2_hdr *hdr; + struct channel *chann; + char signature_req[SMB2_SIGNATURE_SIZE]; + char signature[SMB2_CMACAES_SIZE]; + struct kvec iov[1]; + size_t len; + + hdr = smb2_get_msg(work->request_buf); + if (work->next_smb2_rcv_hdr_off) + hdr = ksmbd_req_buf_next(work); + + if (!hdr->NextCommand && !work->next_smb2_rcv_hdr_off) + len = get_rfc1002_len(work->request_buf); + else if (hdr->NextCommand) + len = le32_to_cpu(hdr->NextCommand); + else + len = get_rfc1002_len(work->request_buf) - + work->next_smb2_rcv_hdr_off; + + if (le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { + signing_key = work->sess->smb3signingkey; + } else { + chann = lookup_chann_list(work->sess, conn); + if (!chann) { + return 0; + } + signing_key = chann->smb3signingkey; + } + + if (!signing_key) { + pr_err("SMB3 signing key is not generated\n"); + return 0; + } + + memcpy(signature_req, hdr->Signature, SMB2_SIGNATURE_SIZE); + memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); + iov[0].iov_base = (char *)&hdr->ProtocolId; + iov[0].iov_len = len; + + if (ksmbd_sign_smb3_pdu(conn, signing_key, iov, 1, signature)) + return 0; + + if (memcmp(signature, signature_req, SMB2_SIGNATURE_SIZE)) { + pr_err("bad smb2 signature\n"); + return 0; + } + + return 1; +} + +/** + * smb3_set_sign_rsp() - handler for rsp packet sign processing + * @work: smb work containing notify command buffer + * + */ +void smb3_set_sign_rsp(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct smb2_hdr *req_hdr, *hdr; + struct channel *chann; + char signature[SMB2_CMACAES_SIZE]; + struct kvec iov[2]; + int n_vec = 1; + size_t len; + char *signing_key; + + hdr = smb2_get_msg(work->response_buf); + if (work->next_smb2_rsp_hdr_off) + hdr = ksmbd_resp_buf_next(work); + + req_hdr = ksmbd_req_buf_next(work); + + if (!work->next_smb2_rsp_hdr_off) { + len = get_rfc1002_len(work->response_buf); + if (req_hdr->NextCommand) + len = ALIGN(len, 8); + } else { + len = get_rfc1002_len(work->response_buf) - + work->next_smb2_rsp_hdr_off; + len = ALIGN(len, 8); + } + + if (conn->binding == false && + le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) { + signing_key = work->sess->smb3signingkey; + } else { + chann = lookup_chann_list(work->sess, work->conn); + if (!chann) { + return; + } + signing_key = chann->smb3signingkey; + } + + if (!signing_key) + return; + + if (req_hdr->NextCommand) + hdr->NextCommand = cpu_to_le32(len); + + hdr->Flags |= SMB2_FLAGS_SIGNED; + memset(hdr->Signature, 0, SMB2_SIGNATURE_SIZE); + iov[0].iov_base = (char *)&hdr->ProtocolId; + iov[0].iov_len = len; + if (work->aux_payload_sz) { + iov[0].iov_len -= work->aux_payload_sz; + iov[1].iov_base = work->aux_payload_buf; + iov[1].iov_len = work->aux_payload_sz; + n_vec++; + } + + if (!ksmbd_sign_smb3_pdu(conn, signing_key, iov, n_vec, signature)) + memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE); +} + +/** + * smb3_preauth_hash_rsp() - handler for computing preauth hash on response + * @work: smb work containing response buffer + * + */ +void smb3_preauth_hash_rsp(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + struct smb2_hdr *req, *rsp; + + if (conn->dialect != SMB311_PROT_ID) + return; + + WORK_BUFFERS(work, req, rsp); + + if (le16_to_cpu(req->Command) == SMB2_NEGOTIATE_HE && + conn->preauth_info) + ksmbd_gen_preauth_integrity_hash(conn, work->response_buf, + conn->preauth_info->Preauth_HashValue); + + if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && sess) { + __u8 *hash_value; + + if (conn->binding) { + struct preauth_session *preauth_sess; + + preauth_sess = ksmbd_preauth_session_lookup(conn, sess->id); + if (!preauth_sess) + return; + hash_value = preauth_sess->Preauth_HashValue; + } else { + hash_value = sess->Preauth_HashValue; + if (!hash_value) + return; + } + ksmbd_gen_preauth_integrity_hash(conn, work->response_buf, + hash_value); + } +} + +static void fill_transform_hdr(void *tr_buf, char *old_buf, __le16 cipher_type) +{ + struct smb2_transform_hdr *tr_hdr = tr_buf + 4; + struct smb2_hdr *hdr = smb2_get_msg(old_buf); + unsigned int orig_len = get_rfc1002_len(old_buf); + + memset(tr_buf, 0, sizeof(struct smb2_transform_hdr) + 4); + tr_hdr->ProtocolId = SMB2_TRANSFORM_PROTO_NUM; + tr_hdr->OriginalMessageSize = cpu_to_le32(orig_len); + tr_hdr->Flags = cpu_to_le16(0x01); + if (cipher_type == SMB2_ENCRYPTION_AES128_GCM || + cipher_type == SMB2_ENCRYPTION_AES256_GCM) + get_random_bytes(&tr_hdr->Nonce, SMB3_AES_GCM_NONCE); + else + get_random_bytes(&tr_hdr->Nonce, SMB3_AES_CCM_NONCE); + memcpy(&tr_hdr->SessionId, &hdr->SessionId, 8); + inc_rfc1001_len(tr_buf, sizeof(struct smb2_transform_hdr)); + inc_rfc1001_len(tr_buf, orig_len); +} + +int smb3_encrypt_resp(struct ksmbd_work *work) +{ + char *buf = work->response_buf; + struct kvec iov[3]; + int rc = -ENOMEM; + int buf_size = 0, rq_nvec = 2 + (work->aux_payload_sz ? 1 : 0); + + if (ARRAY_SIZE(iov) < rq_nvec) + return -ENOMEM; + + work->tr_buf = kzalloc(sizeof(struct smb2_transform_hdr) + 4, GFP_KERNEL); + if (!work->tr_buf) + return rc; + + /* fill transform header */ + fill_transform_hdr(work->tr_buf, buf, work->conn->cipher_type); + + iov[0].iov_base = work->tr_buf; + iov[0].iov_len = sizeof(struct smb2_transform_hdr) + 4; + buf_size += iov[0].iov_len - 4; + + iov[1].iov_base = buf + 4; + iov[1].iov_len = get_rfc1002_len(buf); + if (work->aux_payload_sz) { + iov[1].iov_len = work->resp_hdr_sz - 4; + + iov[2].iov_base = work->aux_payload_buf; + iov[2].iov_len = work->aux_payload_sz; + buf_size += iov[2].iov_len; + } + buf_size += iov[1].iov_len; + work->resp_hdr_sz = iov[1].iov_len; + + rc = ksmbd_crypt_message(work, iov, rq_nvec, 1); + if (rc) + return rc; + + memmove(buf, iov[1].iov_base, iov[1].iov_len); + *(__be32 *)work->tr_buf = cpu_to_be32(buf_size); + + return rc; +} + +bool smb3_is_transform_hdr(void *buf) +{ + struct smb2_transform_hdr *trhdr = smb2_get_msg(buf); + + return trhdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM; +} + +int smb3_decrypt_req(struct ksmbd_work *work) +{ + struct ksmbd_session *sess; + char *buf = work->request_buf; + unsigned int pdu_length = get_rfc1002_len(buf); + struct kvec iov[2]; + int buf_data_size = pdu_length - sizeof(struct smb2_transform_hdr); + struct smb2_transform_hdr *tr_hdr = smb2_get_msg(buf); + int rc = 0; + + if (buf_data_size < sizeof(struct smb2_hdr)) { + pr_err("Transform message is too small (%u)\n", + pdu_length); + return -ECONNABORTED; + } + + if (buf_data_size < le32_to_cpu(tr_hdr->OriginalMessageSize)) { + pr_err("Transform message is broken\n"); + return -ECONNABORTED; + } + + sess = ksmbd_session_lookup_all(work->conn, le64_to_cpu(tr_hdr->SessionId)); + if (!sess) { + pr_err("invalid session id(%llx) in transform header\n", + le64_to_cpu(tr_hdr->SessionId)); + return -ECONNABORTED; + } + + iov[0].iov_base = buf; + iov[0].iov_len = sizeof(struct smb2_transform_hdr) + 4; + iov[1].iov_base = buf + sizeof(struct smb2_transform_hdr) + 4; + iov[1].iov_len = buf_data_size; + rc = ksmbd_crypt_message(work, iov, 2, 0); + if (rc) + return rc; + + memmove(buf + 4, iov[1].iov_base, buf_data_size); + *(__be32 *)buf = cpu_to_be32(buf_data_size); + + return rc; +} + +bool smb3_11_final_sess_setup_resp(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; + struct ksmbd_session *sess = work->sess; + struct smb2_hdr *rsp = smb2_get_msg(work->response_buf); + + if (conn->dialect < SMB30_PROT_ID) + return false; + + if (work->next_smb2_rcv_hdr_off) + rsp = ksmbd_resp_buf_next(work); + + if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && + sess->user && !user_guest(sess->user) && + rsp->Status == STATUS_SUCCESS) + return true; + return false; +} diff --git a/fs/ksmbd/smb2pdu.h b/fs/ksmbd/smb2pdu.h new file mode 100644 index 0000000000000..7a380d1eeafee --- /dev/null +++ b/fs/ksmbd/smb2pdu.h @@ -0,0 +1,1712 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef _SMB2PDU_H +#define _SMB2PDU_H + +#include "ntlmssp.h" +#include "smbacl.h" + +/* + * Note that, due to trying to use names similar to the protocol specifications, + * there are many mixed case field names in the structures below. Although + * this does not match typical Linux kernel style, it is necessary to be + * able to match against the protocol specfication. + * + * SMB2 commands + * Some commands have minimal (wct=0,bcc=0), or uninteresting, responses + * (ie no useful data other than the SMB error code itself) and are marked such. + * Knowing this helps avoid response buffer allocations and copy in some cases. + */ + +/* List of commands in host endian */ +#define SMB2_NEGOTIATE_HE 0x0000 +#define SMB2_SESSION_SETUP_HE 0x0001 +#define SMB2_LOGOFF_HE 0x0002 /* trivial request/resp */ +#define SMB2_TREE_CONNECT_HE 0x0003 +#define SMB2_TREE_DISCONNECT_HE 0x0004 /* trivial req/resp */ +#define SMB2_CREATE_HE 0x0005 +#define SMB2_CLOSE_HE 0x0006 +#define SMB2_FLUSH_HE 0x0007 /* trivial resp */ +#define SMB2_READ_HE 0x0008 +#define SMB2_WRITE_HE 0x0009 +#define SMB2_LOCK_HE 0x000A +#define SMB2_IOCTL_HE 0x000B +#define SMB2_CANCEL_HE 0x000C +#define SMB2_ECHO_HE 0x000D +#define SMB2_QUERY_DIRECTORY_HE 0x000E +#define SMB2_CHANGE_NOTIFY_HE 0x000F +#define SMB2_QUERY_INFO_HE 0x0010 +#define SMB2_SET_INFO_HE 0x0011 +#define SMB2_OPLOCK_BREAK_HE 0x0012 + +/* The same list in little endian */ +#define SMB2_NEGOTIATE cpu_to_le16(SMB2_NEGOTIATE_HE) +#define SMB2_SESSION_SETUP cpu_to_le16(SMB2_SESSION_SETUP_HE) +#define SMB2_LOGOFF cpu_to_le16(SMB2_LOGOFF_HE) +#define SMB2_TREE_CONNECT cpu_to_le16(SMB2_TREE_CONNECT_HE) +#define SMB2_TREE_DISCONNECT cpu_to_le16(SMB2_TREE_DISCONNECT_HE) +#define SMB2_CREATE cpu_to_le16(SMB2_CREATE_HE) +#define SMB2_CLOSE cpu_to_le16(SMB2_CLOSE_HE) +#define SMB2_FLUSH cpu_to_le16(SMB2_FLUSH_HE) +#define SMB2_READ cpu_to_le16(SMB2_READ_HE) +#define SMB2_WRITE cpu_to_le16(SMB2_WRITE_HE) +#define SMB2_LOCK cpu_to_le16(SMB2_LOCK_HE) +#define SMB2_IOCTL cpu_to_le16(SMB2_IOCTL_HE) +#define SMB2_CANCEL cpu_to_le16(SMB2_CANCEL_HE) +#define SMB2_ECHO cpu_to_le16(SMB2_ECHO_HE) +#define SMB2_QUERY_DIRECTORY cpu_to_le16(SMB2_QUERY_DIRECTORY_HE) +#define SMB2_CHANGE_NOTIFY cpu_to_le16(SMB2_CHANGE_NOTIFY_HE) +#define SMB2_QUERY_INFO cpu_to_le16(SMB2_QUERY_INFO_HE) +#define SMB2_SET_INFO cpu_to_le16(SMB2_SET_INFO_HE) +#define SMB2_OPLOCK_BREAK cpu_to_le16(SMB2_OPLOCK_BREAK_HE) + +/*Create Action Flags*/ +#define FILE_SUPERSEDED 0x00000000 +#define FILE_OPENED 0x00000001 +#define FILE_CREATED 0x00000002 +#define FILE_OVERWRITTEN 0x00000003 + +/* + * Size of the session key (crypto key encrypted with the password + */ +#define SMB2_NTLMV2_SESSKEY_SIZE 16 +#define SMB2_SIGNATURE_SIZE 16 +#define SMB2_HMACSHA256_SIZE 32 +#define SMB2_CMACAES_SIZE 16 +#define SMB3_GCM128_CRYPTKEY_SIZE 16 +#define SMB3_GCM256_CRYPTKEY_SIZE 32 + +/* + * Size of the smb3 encryption/decryption keys + */ +#define SMB3_ENC_DEC_KEY_SIZE 32 + +/* + * Size of the smb3 signing key + */ +#define SMB3_SIGN_KEY_SIZE 16 + +#define CIFS_CLIENT_CHALLENGE_SIZE 8 +#define SMB_SERVER_CHALLENGE_SIZE 8 + +/* SMB2 Max Credits */ +#define SMB2_MAX_CREDITS 8192 + +#define SMB2_CLIENT_GUID_SIZE 16 +#define SMB2_CREATE_GUID_SIZE 16 + +/* Maximum buffer size value we can send with 1 credit */ +#define SMB2_MAX_BUFFER_SIZE 65536 + +#define NUMBER_OF_SMB2_COMMANDS 0x0013 + +/* BB FIXME - analyze following length BB */ +#define MAX_SMB2_HDR_SIZE 0x78 /* 4 len + 64 hdr + (2*24 wct) + 2 bct + 2 pad */ + +#define SMB2_PROTO_NUMBER cpu_to_le32(0x424d53fe) /* 'B''M''S' */ +#define SMB2_TRANSFORM_PROTO_NUM cpu_to_le32(0x424d53fd) + +#define SMB21_DEFAULT_IOSIZE (1024 * 1024) +#define SMB3_DEFAULT_IOSIZE (4 * 1024 * 1024) +#define SMB3_DEFAULT_TRANS_SIZE (1024 * 1024) +#define SMB3_MIN_IOSIZE (64 * 1024) +#define SMB3_MAX_IOSIZE (8 * 1024 * 1024) +#define SMB3_MAX_MSGSIZE (4 * 4096) + +/* + * SMB2 Header Definition + * + * "MBZ" : Must be Zero + * "BB" : BugBug, Something to check/review/analyze later + * "PDU" : "Protocol Data Unit" (ie a network "frame") + * + */ + +#define __SMB2_HEADER_STRUCTURE_SIZE 64 +#define SMB2_HEADER_STRUCTURE_SIZE \ + cpu_to_le16(__SMB2_HEADER_STRUCTURE_SIZE) + +struct smb2_hdr { + __le32 ProtocolId; /* 0xFE 'S' 'M' 'B' */ + __le16 StructureSize; /* 64 */ + __le16 CreditCharge; /* MBZ */ + __le32 Status; /* Error from server */ + __le16 Command; + __le16 CreditRequest; /* CreditResponse */ + __le32 Flags; + __le32 NextCommand; + __le64 MessageId; + union { + struct { + __le32 ProcessId; + __le32 TreeId; + } __packed SyncId; + __le64 AsyncId; + } __packed Id; + __le64 SessionId; + __u8 Signature[16]; +} __packed; + +struct smb2_pdu { + struct smb2_hdr hdr; + __le16 StructureSize2; /* size of wct area (varies, request specific) */ +} __packed; + +#define SMB3_AES_CCM_NONCE 11 +#define SMB3_AES_GCM_NONCE 12 + +struct smb2_transform_hdr { + __le32 ProtocolId; /* 0xFD 'S' 'M' 'B' */ + __u8 Signature[16]; + __u8 Nonce[16]; + __le32 OriginalMessageSize; + __u16 Reserved1; + __le16 Flags; /* EncryptionAlgorithm */ + __le64 SessionId; +} __packed; + +/* + * SMB2 flag definitions + */ +#define SMB2_FLAGS_SERVER_TO_REDIR cpu_to_le32(0x00000001) +#define SMB2_FLAGS_ASYNC_COMMAND cpu_to_le32(0x00000002) +#define SMB2_FLAGS_RELATED_OPERATIONS cpu_to_le32(0x00000004) +#define SMB2_FLAGS_SIGNED cpu_to_le32(0x00000008) +#define SMB2_FLAGS_DFS_OPERATIONS cpu_to_le32(0x10000000) +#define SMB2_FLAGS_REPLAY_OPERATIONS cpu_to_le32(0x20000000) + +/* + * Definitions for SMB2 Protocol Data Units (network frames) + * + * See MS-SMB2.PDF specification for protocol details. + * The Naming convention is the lower case version of the SMB2 + * command code name for the struct. Note that structures must be packed. + * + */ + +#define SMB2_ERROR_STRUCTURE_SIZE2 9 +#define SMB2_ERROR_STRUCTURE_SIZE2_LE cpu_to_le16(SMB2_ERROR_STRUCTURE_SIZE2) + +struct smb2_err_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; + __u8 ErrorContextCount; + __u8 Reserved; + __le32 ByteCount; /* even if zero, at least one byte follows */ + __u8 ErrorData[1]; /* variable length */ +} __packed; + +struct smb2_negotiate_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 36 */ + __le16 DialectCount; + __le16 SecurityMode; + __le16 Reserved; /* MBZ */ + __le32 Capabilities; + __u8 ClientGUID[SMB2_CLIENT_GUID_SIZE]; + /* In SMB3.02 and earlier next three were MBZ le64 ClientStartTime */ + __le32 NegotiateContextOffset; /* SMB3.1.1 only. MBZ earlier */ + __le16 NegotiateContextCount; /* SMB3.1.1 only. MBZ earlier */ + __le16 Reserved2; + __le16 Dialects[1]; /* One dialect (vers=) at a time for now */ +} __packed; + +/* SecurityMode flags */ +#define SMB2_NEGOTIATE_SIGNING_ENABLED_LE cpu_to_le16(0x0001) +#define SMB2_NEGOTIATE_SIGNING_REQUIRED 0x0002 +#define SMB2_NEGOTIATE_SIGNING_REQUIRED_LE cpu_to_le16(0x0002) +/* Capabilities flags */ +#define SMB2_GLOBAL_CAP_DFS 0x00000001 +#define SMB2_GLOBAL_CAP_LEASING 0x00000002 /* Resp only New to SMB2.1 */ +#define SMB2_GLOBAL_CAP_LARGE_MTU 0X00000004 /* Resp only New to SMB2.1 */ +#define SMB2_GLOBAL_CAP_MULTI_CHANNEL 0x00000008 /* New to SMB3 */ +#define SMB2_GLOBAL_CAP_PERSISTENT_HANDLES 0x00000010 /* New to SMB3 */ +#define SMB2_GLOBAL_CAP_DIRECTORY_LEASING 0x00000020 /* New to SMB3 */ +#define SMB2_GLOBAL_CAP_ENCRYPTION 0x00000040 /* New to SMB3 */ +/* Internal types */ +#define SMB2_NT_FIND 0x00100000 +#define SMB2_LARGE_FILES 0x00200000 + +#define SMB311_SALT_SIZE 32 +/* Hash Algorithm Types */ +#define SMB2_PREAUTH_INTEGRITY_SHA512 cpu_to_le16(0x0001) + +#define PREAUTH_HASHVALUE_SIZE 64 + +struct preauth_integrity_info { + /* PreAuth integrity Hash ID */ + __le16 Preauth_HashId; + /* PreAuth integrity Hash Value */ + __u8 Preauth_HashValue[PREAUTH_HASHVALUE_SIZE]; +}; + +/* offset is sizeof smb2_negotiate_rsp but rounded up to 8 bytes. */ +#ifdef CONFIG_SMB_SERVER_KERBEROS5 +/* sizeof(struct smb2_negotiate_rsp) = + * header(64) + response(64) + GSS_LENGTH(96) + GSS_PADDING(0) + */ +#define OFFSET_OF_NEG_CONTEXT 0xe0 +#else +/* sizeof(struct smb2_negotiate_rsp) = + * header(64) + response(64) + GSS_LENGTH(74) + GSS_PADDING(6) + */ +#define OFFSET_OF_NEG_CONTEXT 0xd0 +#endif + +#define SMB2_PREAUTH_INTEGRITY_CAPABILITIES cpu_to_le16(1) +#define SMB2_ENCRYPTION_CAPABILITIES cpu_to_le16(2) +#define SMB2_COMPRESSION_CAPABILITIES cpu_to_le16(3) +#define SMB2_NETNAME_NEGOTIATE_CONTEXT_ID cpu_to_le16(5) +#define SMB2_SIGNING_CAPABILITIES cpu_to_le16(8) +#define SMB2_POSIX_EXTENSIONS_AVAILABLE cpu_to_le16(0x100) + +struct smb2_neg_context { + __le16 ContextType; + __le16 DataLength; + __le32 Reserved; + /* Followed by array of data */ +} __packed; + +struct smb2_preauth_neg_context { + __le16 ContextType; /* 1 */ + __le16 DataLength; + __le32 Reserved; + __le16 HashAlgorithmCount; /* 1 */ + __le16 SaltLength; + __le16 HashAlgorithms; /* HashAlgorithms[0] since only one defined */ + __u8 Salt[SMB311_SALT_SIZE]; +} __packed; + +/* Encryption Algorithms Ciphers */ +#define SMB2_ENCRYPTION_AES128_CCM cpu_to_le16(0x0001) +#define SMB2_ENCRYPTION_AES128_GCM cpu_to_le16(0x0002) +#define SMB2_ENCRYPTION_AES256_CCM cpu_to_le16(0x0003) +#define SMB2_ENCRYPTION_AES256_GCM cpu_to_le16(0x0004) + +struct smb2_encryption_neg_context { + __le16 ContextType; /* 2 */ + __le16 DataLength; + __le32 Reserved; + /* CipherCount usally 2, but can be 3 when AES256-GCM enabled */ + __le16 CipherCount; /* AES-128-GCM and AES-128-CCM by default */ + __le16 Ciphers[]; +} __packed; + +#define SMB3_COMPRESS_NONE cpu_to_le16(0x0000) +#define SMB3_COMPRESS_LZNT1 cpu_to_le16(0x0001) +#define SMB3_COMPRESS_LZ77 cpu_to_le16(0x0002) +#define SMB3_COMPRESS_LZ77_HUFF cpu_to_le16(0x0003) + +struct smb2_compression_ctx { + __le16 ContextType; /* 3 */ + __le16 DataLength; + __le32 Reserved; + __le16 CompressionAlgorithmCount; + __u16 Padding; + __le32 Reserved1; + __le16 CompressionAlgorithms[]; +} __packed; + +#define POSIX_CTXT_DATA_LEN 16 +struct smb2_posix_neg_context { + __le16 ContextType; /* 0x100 */ + __le16 DataLength; + __le32 Reserved; + __u8 Name[16]; /* POSIX ctxt GUID 93AD25509CB411E7B42383DE968BCD7C */ +} __packed; + +struct smb2_netname_neg_context { + __le16 ContextType; /* 0x100 */ + __le16 DataLength; + __le32 Reserved; + __le16 NetName[]; /* hostname of target converted to UCS-2 */ +} __packed; + +/* Signing algorithms */ +#define SIGNING_ALG_HMAC_SHA256 cpu_to_le16(0) +#define SIGNING_ALG_AES_CMAC cpu_to_le16(1) +#define SIGNING_ALG_AES_GMAC cpu_to_le16(2) + +struct smb2_signing_capabilities { + __le16 ContextType; /* 8 */ + __le16 DataLength; + __le32 Reserved; + __le16 SigningAlgorithmCount; + __le16 SigningAlgorithms[]; +} __packed; + +struct smb2_negotiate_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 65 */ + __le16 SecurityMode; + __le16 DialectRevision; + __le16 NegotiateContextCount; /* Prior to SMB3.1.1 was Reserved & MBZ */ + __u8 ServerGUID[16]; + __le32 Capabilities; + __le32 MaxTransactSize; + __le32 MaxReadSize; + __le32 MaxWriteSize; + __le64 SystemTime; /* MBZ */ + __le64 ServerStartTime; + __le16 SecurityBufferOffset; + __le16 SecurityBufferLength; + __le32 NegotiateContextOffset; /* Pre:SMB3.1.1 was reserved/ignored */ + __u8 Buffer[1]; /* variable length GSS security buffer */ +} __packed; + +/* Flags */ +#define SMB2_SESSION_REQ_FLAG_BINDING 0x01 +#define SMB2_SESSION_REQ_FLAG_ENCRYPT_DATA 0x04 + +#define SMB2_SESSION_EXPIRED (0) +#define SMB2_SESSION_IN_PROGRESS BIT(0) +#define SMB2_SESSION_VALID BIT(1) + +/* Flags */ +#define SMB2_SESSION_REQ_FLAG_BINDING 0x01 +#define SMB2_SESSION_REQ_FLAG_ENCRYPT_DATA 0x04 + +struct smb2_sess_setup_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 25 */ + __u8 Flags; + __u8 SecurityMode; + __le32 Capabilities; + __le32 Channel; + __le16 SecurityBufferOffset; + __le16 SecurityBufferLength; + __le64 PreviousSessionId; + __u8 Buffer[1]; /* variable length GSS security buffer */ +} __packed; + +/* Flags/Reserved for SMB3.1.1 */ +#define SMB2_SHAREFLAG_CLUSTER_RECONNECT 0x0001 + +/* Currently defined SessionFlags */ +#define SMB2_SESSION_FLAG_IS_GUEST_LE cpu_to_le16(0x0001) +#define SMB2_SESSION_FLAG_IS_NULL_LE cpu_to_le16(0x0002) +#define SMB2_SESSION_FLAG_ENCRYPT_DATA_LE cpu_to_le16(0x0004) +struct smb2_sess_setup_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 SessionFlags; + __le16 SecurityBufferOffset; + __le16 SecurityBufferLength; + __u8 Buffer[1]; /* variable length GSS security buffer */ +} __packed; + +struct smb2_logoff_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + +struct smb2_logoff_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + +struct smb2_tree_connect_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 Reserved; /* Flags in SMB3.1.1 */ + __le16 PathOffset; + __le16 PathLength; + __u8 Buffer[1]; /* variable length */ +} __packed; + +struct smb2_tree_connect_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 16 */ + __u8 ShareType; /* see below */ + __u8 Reserved; + __le32 ShareFlags; /* see below */ + __le32 Capabilities; /* see below */ + __le32 MaximalAccess; +} __packed; + +/* Possible ShareType values */ +#define SMB2_SHARE_TYPE_DISK 0x01 +#define SMB2_SHARE_TYPE_PIPE 0x02 +#define SMB2_SHARE_TYPE_PRINT 0x03 + +/* + * Possible ShareFlags - exactly one and only one of the first 4 caching flags + * must be set (any of the remaining, SHI1005, flags may be set individually + * or in combination. + */ +#define SMB2_SHAREFLAG_MANUAL_CACHING 0x00000000 +#define SMB2_SHAREFLAG_AUTO_CACHING 0x00000010 +#define SMB2_SHAREFLAG_VDO_CACHING 0x00000020 +#define SMB2_SHAREFLAG_NO_CACHING 0x00000030 +#define SHI1005_FLAGS_DFS 0x00000001 +#define SHI1005_FLAGS_DFS_ROOT 0x00000002 +#define SHI1005_FLAGS_RESTRICT_EXCLUSIVE_OPENS 0x00000100 +#define SHI1005_FLAGS_FORCE_SHARED_DELETE 0x00000200 +#define SHI1005_FLAGS_ALLOW_NAMESPACE_CACHING 0x00000400 +#define SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM 0x00000800 +#define SHI1005_FLAGS_FORCE_LEVELII_OPLOCK 0x00001000 +#define SHI1005_FLAGS_ENABLE_HASH 0x00002000 + +/* Possible share capabilities */ +#define SMB2_SHARE_CAP_DFS cpu_to_le32(0x00000008) + +struct smb2_tree_disconnect_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + +struct smb2_tree_disconnect_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + +#define ATTR_READONLY_LE cpu_to_le32(ATTR_READONLY) +#define ATTR_HIDDEN_LE cpu_to_le32(ATTR_HIDDEN) +#define ATTR_SYSTEM_LE cpu_to_le32(ATTR_SYSTEM) +#define ATTR_DIRECTORY_LE cpu_to_le32(ATTR_DIRECTORY) +#define ATTR_ARCHIVE_LE cpu_to_le32(ATTR_ARCHIVE) +#define ATTR_NORMAL_LE cpu_to_le32(ATTR_NORMAL) +#define ATTR_TEMPORARY_LE cpu_to_le32(ATTR_TEMPORARY) +#define ATTR_SPARSE_FILE_LE cpu_to_le32(ATTR_SPARSE) +#define ATTR_REPARSE_POINT_LE cpu_to_le32(ATTR_REPARSE) +#define ATTR_COMPRESSED_LE cpu_to_le32(ATTR_COMPRESSED) +#define ATTR_OFFLINE_LE cpu_to_le32(ATTR_OFFLINE) +#define ATTR_NOT_CONTENT_INDEXED_LE cpu_to_le32(ATTR_NOT_CONTENT_INDEXED) +#define ATTR_ENCRYPTED_LE cpu_to_le32(ATTR_ENCRYPTED) +#define ATTR_INTEGRITY_STREAML_LE cpu_to_le32(0x00008000) +#define ATTR_NO_SCRUB_DATA_LE cpu_to_le32(0x00020000) +#define ATTR_MASK_LE cpu_to_le32(0x00007FB7) + +/* Oplock levels */ +#define SMB2_OPLOCK_LEVEL_NONE 0x00 +#define SMB2_OPLOCK_LEVEL_II 0x01 +#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08 +#define SMB2_OPLOCK_LEVEL_BATCH 0x09 +#define SMB2_OPLOCK_LEVEL_LEASE 0xFF +/* Non-spec internal type */ +#define SMB2_OPLOCK_LEVEL_NOCHANGE 0x99 + +/* Desired Access Flags */ +#define FILE_READ_DATA_LE cpu_to_le32(0x00000001) +#define FILE_LIST_DIRECTORY_LE cpu_to_le32(0x00000001) +#define FILE_WRITE_DATA_LE cpu_to_le32(0x00000002) +#define FILE_ADD_FILE_LE cpu_to_le32(0x00000002) +#define FILE_APPEND_DATA_LE cpu_to_le32(0x00000004) +#define FILE_ADD_SUBDIRECTORY_LE cpu_to_le32(0x00000004) +#define FILE_READ_EA_LE cpu_to_le32(0x00000008) +#define FILE_WRITE_EA_LE cpu_to_le32(0x00000010) +#define FILE_EXECUTE_LE cpu_to_le32(0x00000020) +#define FILE_TRAVERSE_LE cpu_to_le32(0x00000020) +#define FILE_DELETE_CHILD_LE cpu_to_le32(0x00000040) +#define FILE_READ_ATTRIBUTES_LE cpu_to_le32(0x00000080) +#define FILE_WRITE_ATTRIBUTES_LE cpu_to_le32(0x00000100) +#define FILE_DELETE_LE cpu_to_le32(0x00010000) +#define FILE_READ_CONTROL_LE cpu_to_le32(0x00020000) +#define FILE_WRITE_DAC_LE cpu_to_le32(0x00040000) +#define FILE_WRITE_OWNER_LE cpu_to_le32(0x00080000) +#define FILE_SYNCHRONIZE_LE cpu_to_le32(0x00100000) +#define FILE_ACCESS_SYSTEM_SECURITY_LE cpu_to_le32(0x01000000) +#define FILE_MAXIMAL_ACCESS_LE cpu_to_le32(0x02000000) +#define FILE_GENERIC_ALL_LE cpu_to_le32(0x10000000) +#define FILE_GENERIC_EXECUTE_LE cpu_to_le32(0x20000000) +#define FILE_GENERIC_WRITE_LE cpu_to_le32(0x40000000) +#define FILE_GENERIC_READ_LE cpu_to_le32(0x80000000) +#define DESIRED_ACCESS_MASK cpu_to_le32(0xF21F01FF) + +/* ShareAccess Flags */ +#define FILE_SHARE_READ_LE cpu_to_le32(0x00000001) +#define FILE_SHARE_WRITE_LE cpu_to_le32(0x00000002) +#define FILE_SHARE_DELETE_LE cpu_to_le32(0x00000004) +#define FILE_SHARE_ALL_LE cpu_to_le32(0x00000007) + +/* CreateDisposition Flags */ +#define FILE_SUPERSEDE_LE cpu_to_le32(0x00000000) +#define FILE_OPEN_LE cpu_to_le32(0x00000001) +#define FILE_CREATE_LE cpu_to_le32(0x00000002) +#define FILE_OPEN_IF_LE cpu_to_le32(0x00000003) +#define FILE_OVERWRITE_LE cpu_to_le32(0x00000004) +#define FILE_OVERWRITE_IF_LE cpu_to_le32(0x00000005) +#define FILE_CREATE_MASK_LE cpu_to_le32(0x00000007) + +#define FILE_READ_DESIRED_ACCESS_LE (FILE_READ_DATA_LE | \ + FILE_READ_EA_LE | \ + FILE_GENERIC_READ_LE) +#define FILE_WRITE_DESIRE_ACCESS_LE (FILE_WRITE_DATA_LE | \ + FILE_APPEND_DATA_LE | \ + FILE_WRITE_EA_LE | \ + FILE_WRITE_ATTRIBUTES_LE | \ + FILE_GENERIC_WRITE_LE) + +/* Impersonation Levels */ +#define IL_ANONYMOUS_LE cpu_to_le32(0x00000000) +#define IL_IDENTIFICATION_LE cpu_to_le32(0x00000001) +#define IL_IMPERSONATION_LE cpu_to_le32(0x00000002) +#define IL_DELEGATE_LE cpu_to_le32(0x00000003) + +/* Create Context Values */ +#define SMB2_CREATE_EA_BUFFER "ExtA" /* extended attributes */ +#define SMB2_CREATE_SD_BUFFER "SecD" /* security descriptor */ +#define SMB2_CREATE_DURABLE_HANDLE_REQUEST "DHnQ" +#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT "DHnC" +#define SMB2_CREATE_ALLOCATION_SIZE "AlSi" +#define SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST "MxAc" +#define SMB2_CREATE_TIMEWARP_REQUEST "TWrp" +#define SMB2_CREATE_QUERY_ON_DISK_ID "QFid" +#define SMB2_CREATE_REQUEST_LEASE "RqLs" +#define SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2 "DH2Q" +#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 "DH2C" +#define SMB2_CREATE_APP_INSTANCE_ID "\x45\xBC\xA6\x6A\xEF\xA7\xF7\x4A\x90\x08\xFA\x46\x2E\x14\x4D\x74" + #define SMB2_CREATE_APP_INSTANCE_VERSION "\xB9\x82\xD0\xB7\x3B\x56\x07\x4F\xA0\x7B\x52\x4A\x81\x16\xA0\x10" +#define SVHDX_OPEN_DEVICE_CONTEXT 0x83CE6F1AD851E0986E34401CC9BCFCE9 +#define SMB2_CREATE_TAG_POSIX "\x93\xAD\x25\x50\x9C\xB4\x11\xE7\xB4\x23\x83\xDE\x96\x8B\xCD\x7C" + +struct smb2_create_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 57 */ + __u8 SecurityFlags; + __u8 RequestedOplockLevel; + __le32 ImpersonationLevel; + __le64 SmbCreateFlags; + __le64 Reserved; + __le32 DesiredAccess; + __le32 FileAttributes; + __le32 ShareAccess; + __le32 CreateDisposition; + __le32 CreateOptions; + __le16 NameOffset; + __le16 NameLength; + __le32 CreateContextsOffset; + __le32 CreateContextsLength; + __u8 Buffer[0]; +} __packed; + +struct smb2_create_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 89 */ + __u8 OplockLevel; + __u8 Reserved; + __le32 CreateAction; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 AllocationSize; + __le64 EndofFile; + __le32 FileAttributes; + __le32 Reserved2; + __u64 PersistentFileId; + __u64 VolatileFileId; + __le32 CreateContextsOffset; + __le32 CreateContextsLength; + __u8 Buffer[1]; +} __packed; + +struct create_context { + __le32 Next; + __le16 NameOffset; + __le16 NameLength; + __le16 Reserved; + __le16 DataOffset; + __le32 DataLength; + __u8 Buffer[0]; +} __packed; + +struct create_durable_req_v2 { + struct create_context ccontext; + __u8 Name[8]; + __le32 Timeout; + __le32 Flags; + __u8 Reserved[8]; + __u8 CreateGuid[16]; +} __packed; + +struct create_durable_reconn_req { + struct create_context ccontext; + __u8 Name[8]; + union { + __u8 Reserved[16]; + struct { + __u64 PersistentFileId; + __u64 VolatileFileId; + } Fid; + } Data; +} __packed; + +struct create_durable_reconn_v2_req { + struct create_context ccontext; + __u8 Name[8]; + struct { + __u64 PersistentFileId; + __u64 VolatileFileId; + } Fid; + __u8 CreateGuid[16]; + __le32 Flags; +} __packed; + +struct create_app_inst_id { + struct create_context ccontext; + __u8 Name[8]; + __u8 Reserved[8]; + __u8 AppInstanceId[16]; +} __packed; + +struct create_app_inst_id_vers { + struct create_context ccontext; + __u8 Name[8]; + __u8 Reserved[2]; + __u8 Padding[4]; + __le64 AppInstanceVersionHigh; + __le64 AppInstanceVersionLow; +} __packed; + +struct create_mxac_req { + struct create_context ccontext; + __u8 Name[8]; + __le64 Timestamp; +} __packed; + +struct create_alloc_size_req { + struct create_context ccontext; + __u8 Name[8]; + __le64 AllocationSize; +} __packed; + +struct create_posix { + struct create_context ccontext; + __u8 Name[16]; + __le32 Mode; + __u32 Reserved; +} __packed; + +struct create_durable_rsp { + struct create_context ccontext; + __u8 Name[8]; + union { + __u8 Reserved[8]; + __u64 data; + } Data; +} __packed; + +struct create_durable_v2_rsp { + struct create_context ccontext; + __u8 Name[8]; + __le32 Timeout; + __le32 Flags; +} __packed; + +struct create_mxac_rsp { + struct create_context ccontext; + __u8 Name[8]; + __le32 QueryStatus; + __le32 MaximalAccess; +} __packed; + +struct create_disk_id_rsp { + struct create_context ccontext; + __u8 Name[8]; + __le64 DiskFileId; + __le64 VolumeId; + __u8 Reserved[16]; +} __packed; + +/* equivalent of the contents of SMB3.1.1 POSIX open context response */ +struct create_posix_rsp { + struct create_context ccontext; + __u8 Name[16]; + __le32 nlink; + __le32 reparse_tag; + __le32 mode; + /* SidBuffer contain two sids(Domain sid(28), UNIX group sid(16)) */ + u8 SidBuffer[44]; +} __packed; + +#define SMB2_LEASE_NONE_LE cpu_to_le32(0x00) +#define SMB2_LEASE_READ_CACHING_LE cpu_to_le32(0x01) +#define SMB2_LEASE_HANDLE_CACHING_LE cpu_to_le32(0x02) +#define SMB2_LEASE_WRITE_CACHING_LE cpu_to_le32(0x04) + +#define SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE cpu_to_le32(0x02) + +#define SMB2_LEASE_KEY_SIZE 16 + +struct lease_context { + __u8 LeaseKey[SMB2_LEASE_KEY_SIZE]; + __le32 LeaseState; + __le32 LeaseFlags; + __le64 LeaseDuration; +} __packed; + +struct lease_context_v2 { + __u8 LeaseKey[SMB2_LEASE_KEY_SIZE]; + __le32 LeaseState; + __le32 LeaseFlags; + __le64 LeaseDuration; + __u8 ParentLeaseKey[SMB2_LEASE_KEY_SIZE]; + __le16 Epoch; + __le16 Reserved; +} __packed; + +struct create_lease { + struct create_context ccontext; + __u8 Name[8]; + struct lease_context lcontext; +} __packed; + +struct create_lease_v2 { + struct create_context ccontext; + __u8 Name[8]; + struct lease_context_v2 lcontext; + __u8 Pad[4]; +} __packed; + +/* Currently defined values for close flags */ +#define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB cpu_to_le16(0x0001) +struct smb2_close_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 24 */ + __le16 Flags; + __le32 Reserved; + __u64 PersistentFileId; + __u64 VolatileFileId; +} __packed; + +struct smb2_close_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* 60 */ + __le16 Flags; + __le32 Reserved; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 AllocationSize; /* Beginning of FILE_STANDARD_INFO equivalent */ + __le64 EndOfFile; + __le32 Attributes; +} __packed; + +struct smb2_flush_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 24 */ + __le16 Reserved1; + __le32 Reserved2; + __u64 PersistentFileId; + __u64 VolatileFileId; +} __packed; + +struct smb2_flush_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; + __le16 Reserved; +} __packed; + +struct smb2_buffer_desc_v1 { + __le64 offset; + __le32 token; + __le32 length; +} __packed; + +#define SMB2_CHANNEL_NONE cpu_to_le32(0x00000000) +#define SMB2_CHANNEL_RDMA_V1 cpu_to_le32(0x00000001) +#define SMB2_CHANNEL_RDMA_V1_INVALIDATE cpu_to_le32(0x00000002) + +struct smb2_read_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 49 */ + __u8 Padding; /* offset from start of SMB2 header to place read */ + __u8 Reserved; + __le32 Length; + __le64 Offset; + __u64 PersistentFileId; + __u64 VolatileFileId; + __le32 MinimumCount; + __le32 Channel; /* Reserved MBZ */ + __le32 RemainingBytes; + __le16 ReadChannelInfoOffset; /* Reserved MBZ */ + __le16 ReadChannelInfoLength; /* Reserved MBZ */ + __u8 Buffer[1]; +} __packed; + +struct smb2_read_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 17 */ + __u8 DataOffset; + __u8 Reserved; + __le32 DataLength; + __le32 DataRemaining; + __u32 Reserved2; + __u8 Buffer[1]; +} __packed; + +/* For write request Flags field below the following flag is defined: */ +#define SMB2_WRITEFLAG_WRITE_THROUGH 0x00000001 + +struct smb2_write_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 49 */ + __le16 DataOffset; /* offset from start of SMB2 header to write data */ + __le32 Length; + __le64 Offset; + __u64 PersistentFileId; + __u64 VolatileFileId; + __le32 Channel; /* Reserved MBZ */ + __le32 RemainingBytes; + __le16 WriteChannelInfoOffset; /* Reserved MBZ */ + __le16 WriteChannelInfoLength; /* Reserved MBZ */ + __le32 Flags; + __u8 Buffer[1]; +} __packed; + +struct smb2_write_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 17 */ + __u8 DataOffset; + __u8 Reserved; + __le32 DataLength; + __le32 DataRemaining; + __u32 Reserved2; + __u8 Buffer[1]; +} __packed; + +#define SMB2_0_IOCTL_IS_FSCTL 0x00000001 + +struct duplicate_extents_to_file { + __u64 PersistentFileHandle; /* source file handle, opaque endianness */ + __u64 VolatileFileHandle; + __le64 SourceFileOffset; + __le64 TargetFileOffset; + __le64 ByteCount; /* Bytes to be copied */ +} __packed; + +struct smb2_ioctl_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 57 */ + __le16 Reserved; /* offset from start of SMB2 header to write data */ + __le32 CntCode; + __u64 PersistentFileId; + __u64 VolatileFileId; + __le32 InputOffset; /* Reserved MBZ */ + __le32 InputCount; + __le32 MaxInputResponse; + __le32 OutputOffset; + __le32 OutputCount; + __le32 MaxOutputResponse; + __le32 Flags; + __le32 Reserved2; + __u8 Buffer[1]; +} __packed; + +struct smb2_ioctl_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 49 */ + __le16 Reserved; /* offset from start of SMB2 header to write data */ + __le32 CntCode; + __u64 PersistentFileId; + __u64 VolatileFileId; + __le32 InputOffset; /* Reserved MBZ */ + __le32 InputCount; + __le32 OutputOffset; + __le32 OutputCount; + __le32 Flags; + __le32 Reserved2; + __u8 Buffer[1]; +} __packed; + +struct validate_negotiate_info_req { + __le32 Capabilities; + __u8 Guid[SMB2_CLIENT_GUID_SIZE]; + __le16 SecurityMode; + __le16 DialectCount; + __le16 Dialects[1]; /* dialect (someday maybe list) client asked for */ +} __packed; + +struct validate_negotiate_info_rsp { + __le32 Capabilities; + __u8 Guid[SMB2_CLIENT_GUID_SIZE]; + __le16 SecurityMode; + __le16 Dialect; /* Dialect in use for the connection */ +} __packed; + +struct smb_sockaddr_in { + __be16 Port; + __be32 IPv4address; + __u8 Reserved[8]; +} __packed; + +struct smb_sockaddr_in6 { + __be16 Port; + __be32 FlowInfo; + __u8 IPv6address[16]; + __be32 ScopeId; +} __packed; + +#define INTERNETWORK 0x0002 +#define INTERNETWORKV6 0x0017 + +struct sockaddr_storage_rsp { + __le16 Family; + union { + struct smb_sockaddr_in addr4; + struct smb_sockaddr_in6 addr6; + }; +} __packed; + +#define RSS_CAPABLE 0x00000001 +#define RDMA_CAPABLE 0x00000002 + +struct network_interface_info_ioctl_rsp { + __le32 Next; /* next interface. zero if this is last one */ + __le32 IfIndex; + __le32 Capability; /* RSS or RDMA Capable */ + __le32 Reserved; + __le64 LinkSpeed; + char SockAddr_Storage[128]; +} __packed; + +struct file_object_buf_type1_ioctl_rsp { + __u8 ObjectId[16]; + __u8 BirthVolumeId[16]; + __u8 BirthObjectId[16]; + __u8 DomainId[16]; +} __packed; + +struct resume_key_ioctl_rsp { + __u64 ResumeKey[3]; + __le32 ContextLength; + __u8 Context[4]; /* ignored, Windows sets to 4 bytes of zero */ +} __packed; + +struct copychunk_ioctl_req { + __le64 ResumeKey[3]; + __le32 ChunkCount; + __le32 Reserved; + __u8 Chunks[1]; /* array of srv_copychunk */ +} __packed; + +struct srv_copychunk { + __le64 SourceOffset; + __le64 TargetOffset; + __le32 Length; + __le32 Reserved; +} __packed; + +struct copychunk_ioctl_rsp { + __le32 ChunksWritten; + __le32 ChunkBytesWritten; + __le32 TotalBytesWritten; +} __packed; + +struct file_sparse { + __u8 SetSparse; +} __packed; + +struct file_zero_data_information { + __le64 FileOffset; + __le64 BeyondFinalZero; +} __packed; + +struct file_allocated_range_buffer { + __le64 file_offset; + __le64 length; +} __packed; + +struct reparse_data_buffer { + __le32 ReparseTag; + __le16 ReparseDataLength; + __u16 Reserved; + __u8 DataBuffer[]; /* Variable Length */ +} __packed; + +/* Completion Filter flags for Notify */ +#define FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001 +#define FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002 +#define FILE_NOTIFY_CHANGE_NAME 0x00000003 +#define FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004 +#define FILE_NOTIFY_CHANGE_SIZE 0x00000008 +#define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010 +#define FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020 +#define FILE_NOTIFY_CHANGE_CREATION 0x00000040 +#define FILE_NOTIFY_CHANGE_EA 0x00000080 +#define FILE_NOTIFY_CHANGE_SECURITY 0x00000100 +#define FILE_NOTIFY_CHANGE_STREAM_NAME 0x00000200 +#define FILE_NOTIFY_CHANGE_STREAM_SIZE 0x00000400 +#define FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800 + +/* Flags */ +#define SMB2_WATCH_TREE 0x0001 + +struct smb2_notify_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 32 */ + __le16 Flags; + __le32 OutputBufferLength; + __u64 PersistentFileId; + __u64 VolatileFileId; + __u32 CompletionFileter; + __u32 Reserved; +} __packed; + +struct smb2_notify_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 OutputBufferOffset; + __le32 OutputBufferLength; + __u8 Buffer[1]; +} __packed; + +/* SMB2 Notify Action Flags */ +#define FILE_ACTION_ADDED 0x00000001 +#define FILE_ACTION_REMOVED 0x00000002 +#define FILE_ACTION_MODIFIED 0x00000003 +#define FILE_ACTION_RENAMED_OLD_NAME 0x00000004 +#define FILE_ACTION_RENAMED_NEW_NAME 0x00000005 +#define FILE_ACTION_ADDED_STREAM 0x00000006 +#define FILE_ACTION_REMOVED_STREAM 0x00000007 +#define FILE_ACTION_MODIFIED_STREAM 0x00000008 +#define FILE_ACTION_REMOVED_BY_DELETE 0x00000009 + +#define SMB2_LOCKFLAG_SHARED 0x0001 +#define SMB2_LOCKFLAG_EXCLUSIVE 0x0002 +#define SMB2_LOCKFLAG_UNLOCK 0x0004 +#define SMB2_LOCKFLAG_FAIL_IMMEDIATELY 0x0010 +#define SMB2_LOCKFLAG_MASK 0x0007 + +struct smb2_lock_element { + __le64 Offset; + __le64 Length; + __le32 Flags; + __le32 Reserved; +} __packed; + +struct smb2_lock_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 48 */ + __le16 LockCount; + __le32 Reserved; + __u64 PersistentFileId; + __u64 VolatileFileId; + /* Followed by at least one */ + struct smb2_lock_element locks[1]; +} __packed; + +struct smb2_lock_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; +} __packed; + +struct smb2_echo_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __u16 Reserved; +} __packed; + +struct smb2_echo_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __u16 Reserved; +} __packed; + +/* search (query_directory) Flags field */ +#define SMB2_RESTART_SCANS 0x01 +#define SMB2_RETURN_SINGLE_ENTRY 0x02 +#define SMB2_INDEX_SPECIFIED 0x04 +#define SMB2_REOPEN 0x10 + +struct smb2_query_directory_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 33 */ + __u8 FileInformationClass; + __u8 Flags; + __le32 FileIndex; + __u64 PersistentFileId; + __u64 VolatileFileId; + __le16 FileNameOffset; + __le16 FileNameLength; + __le32 OutputBufferLength; + __u8 Buffer[1]; +} __packed; + +struct smb2_query_directory_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 OutputBufferOffset; + __le32 OutputBufferLength; + __u8 Buffer[1]; +} __packed; + +/* Possible InfoType values */ +#define SMB2_O_INFO_FILE 0x01 +#define SMB2_O_INFO_FILESYSTEM 0x02 +#define SMB2_O_INFO_SECURITY 0x03 +#define SMB2_O_INFO_QUOTA 0x04 + +/* Security info type additionalinfo flags. See MS-SMB2 (2.2.37) or MS-DTYP */ +#define OWNER_SECINFO 0x00000001 +#define GROUP_SECINFO 0x00000002 +#define DACL_SECINFO 0x00000004 +#define SACL_SECINFO 0x00000008 +#define LABEL_SECINFO 0x00000010 +#define ATTRIBUTE_SECINFO 0x00000020 +#define SCOPE_SECINFO 0x00000040 +#define BACKUP_SECINFO 0x00010000 +#define UNPROTECTED_SACL_SECINFO 0x10000000 +#define UNPROTECTED_DACL_SECINFO 0x20000000 +#define PROTECTED_SACL_SECINFO 0x40000000 +#define PROTECTED_DACL_SECINFO 0x80000000 + +struct smb2_query_info_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 41 */ + __u8 InfoType; + __u8 FileInfoClass; + __le32 OutputBufferLength; + __le16 InputBufferOffset; + __u16 Reserved; + __le32 InputBufferLength; + __le32 AdditionalInformation; + __le32 Flags; + __u64 PersistentFileId; + __u64 VolatileFileId; + __u8 Buffer[1]; +} __packed; + +struct smb2_query_info_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 OutputBufferOffset; + __le32 OutputBufferLength; + __u8 Buffer[1]; +} __packed; + +struct smb2_set_info_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 33 */ + __u8 InfoType; + __u8 FileInfoClass; + __le32 BufferLength; + __le16 BufferOffset; + __u16 Reserved; + __le32 AdditionalInformation; + __u64 PersistentFileId; + __u64 VolatileFileId; + __u8 Buffer[1]; +} __packed; + +struct smb2_set_info_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 2 */ +} __packed; + +/* FILE Info response size */ +#define FILE_DIRECTORY_INFORMATION_SIZE 1 +#define FILE_FULL_DIRECTORY_INFORMATION_SIZE 2 +#define FILE_BOTH_DIRECTORY_INFORMATION_SIZE 3 +#define FILE_BASIC_INFORMATION_SIZE 40 +#define FILE_STANDARD_INFORMATION_SIZE 24 +#define FILE_INTERNAL_INFORMATION_SIZE 8 +#define FILE_EA_INFORMATION_SIZE 4 +#define FILE_ACCESS_INFORMATION_SIZE 4 +#define FILE_NAME_INFORMATION_SIZE 9 +#define FILE_RENAME_INFORMATION_SIZE 10 +#define FILE_LINK_INFORMATION_SIZE 11 +#define FILE_NAMES_INFORMATION_SIZE 12 +#define FILE_DISPOSITION_INFORMATION_SIZE 13 +#define FILE_POSITION_INFORMATION_SIZE 14 +#define FILE_FULL_EA_INFORMATION_SIZE 15 +#define FILE_MODE_INFORMATION_SIZE 4 +#define FILE_ALIGNMENT_INFORMATION_SIZE 4 +#define FILE_ALL_INFORMATION_SIZE 104 +#define FILE_ALLOCATION_INFORMATION_SIZE 19 +#define FILE_END_OF_FILE_INFORMATION_SIZE 20 +#define FILE_ALTERNATE_NAME_INFORMATION_SIZE 8 +#define FILE_STREAM_INFORMATION_SIZE 32 +#define FILE_PIPE_INFORMATION_SIZE 23 +#define FILE_PIPE_LOCAL_INFORMATION_SIZE 24 +#define FILE_PIPE_REMOTE_INFORMATION_SIZE 25 +#define FILE_MAILSLOT_QUERY_INFORMATION_SIZE 26 +#define FILE_MAILSLOT_SET_INFORMATION_SIZE 27 +#define FILE_COMPRESSION_INFORMATION_SIZE 16 +#define FILE_OBJECT_ID_INFORMATION_SIZE 29 +/* Number 30 not defined in documents */ +#define FILE_MOVE_CLUSTER_INFORMATION_SIZE 31 +#define FILE_QUOTA_INFORMATION_SIZE 32 +#define FILE_REPARSE_POINT_INFORMATION_SIZE 33 +#define FILE_NETWORK_OPEN_INFORMATION_SIZE 56 +#define FILE_ATTRIBUTE_TAG_INFORMATION_SIZE 8 + +/* FS Info response size */ +#define FS_DEVICE_INFORMATION_SIZE 8 +#define FS_ATTRIBUTE_INFORMATION_SIZE 16 +#define FS_VOLUME_INFORMATION_SIZE 24 +#define FS_SIZE_INFORMATION_SIZE 24 +#define FS_FULL_SIZE_INFORMATION_SIZE 32 +#define FS_SECTOR_SIZE_INFORMATION_SIZE 28 +#define FS_OBJECT_ID_INFORMATION_SIZE 64 +#define FS_CONTROL_INFORMATION_SIZE 48 +#define FS_POSIX_INFORMATION_SIZE 56 + +/* FS_ATTRIBUTE_File_System_Name */ +#define FS_TYPE_SUPPORT_SIZE 44 +struct fs_type_info { + char *fs_name; + long magic_number; +} __packed; + +struct smb2_oplock_break { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 24 */ + __u8 OplockLevel; + __u8 Reserved; + __le32 Reserved2; + __le64 PersistentFid; + __le64 VolatileFid; +} __packed; + +#define SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED cpu_to_le32(0x01) + +struct smb2_lease_break { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 44 */ + __le16 Epoch; + __le32 Flags; + __u8 LeaseKey[16]; + __le32 CurrentLeaseState; + __le32 NewLeaseState; + __le32 BreakReason; + __le32 AccessMaskHint; + __le32 ShareMaskHint; +} __packed; + +struct smb2_lease_ack { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 36 */ + __le16 Reserved; + __le32 Flags; + __u8 LeaseKey[16]; + __le32 LeaseState; + __le64 LeaseDuration; +} __packed; + +/* + * PDU infolevel structure definitions + * BB consider moving to a different header + */ + +/* File System Information Classes */ +#define FS_VOLUME_INFORMATION 1 /* Query */ +#define FS_LABEL_INFORMATION 2 /* Set */ +#define FS_SIZE_INFORMATION 3 /* Query */ +#define FS_DEVICE_INFORMATION 4 /* Query */ +#define FS_ATTRIBUTE_INFORMATION 5 /* Query */ +#define FS_CONTROL_INFORMATION 6 /* Query, Set */ +#define FS_FULL_SIZE_INFORMATION 7 /* Query */ +#define FS_OBJECT_ID_INFORMATION 8 /* Query, Set */ +#define FS_DRIVER_PATH_INFORMATION 9 /* Query */ +#define FS_SECTOR_SIZE_INFORMATION 11 /* SMB3 or later. Query */ +#define FS_POSIX_INFORMATION 100 /* SMB3.1.1 POSIX. Query */ + +struct smb2_fs_full_size_info { + __le64 TotalAllocationUnits; + __le64 CallerAvailableAllocationUnits; + __le64 ActualAvailableAllocationUnits; + __le32 SectorsPerAllocationUnit; + __le32 BytesPerSector; +} __packed; + +#define SSINFO_FLAGS_ALIGNED_DEVICE 0x00000001 +#define SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE 0x00000002 +#define SSINFO_FLAGS_NO_SEEK_PENALTY 0x00000004 +#define SSINFO_FLAGS_TRIM_ENABLED 0x00000008 + +/* sector size info struct */ +struct smb3_fs_ss_info { + __le32 LogicalBytesPerSector; + __le32 PhysicalBytesPerSectorForAtomicity; + __le32 PhysicalBytesPerSectorForPerf; + __le32 FSEffPhysicalBytesPerSectorForAtomicity; + __le32 Flags; + __le32 ByteOffsetForSectorAlignment; + __le32 ByteOffsetForPartitionAlignment; +} __packed; + +/* File System Control Information */ +struct smb2_fs_control_info { + __le64 FreeSpaceStartFiltering; + __le64 FreeSpaceThreshold; + __le64 FreeSpaceStopFiltering; + __le64 DefaultQuotaThreshold; + __le64 DefaultQuotaLimit; + __le32 FileSystemControlFlags; + __le32 Padding; +} __packed; + +/* partial list of QUERY INFO levels */ +#define FILE_DIRECTORY_INFORMATION 1 +#define FILE_FULL_DIRECTORY_INFORMATION 2 +#define FILE_BOTH_DIRECTORY_INFORMATION 3 +#define FILE_BASIC_INFORMATION 4 +#define FILE_STANDARD_INFORMATION 5 +#define FILE_INTERNAL_INFORMATION 6 +#define FILE_EA_INFORMATION 7 +#define FILE_ACCESS_INFORMATION 8 +#define FILE_NAME_INFORMATION 9 +#define FILE_RENAME_INFORMATION 10 +#define FILE_LINK_INFORMATION 11 +#define FILE_NAMES_INFORMATION 12 +#define FILE_DISPOSITION_INFORMATION 13 +#define FILE_POSITION_INFORMATION 14 +#define FILE_FULL_EA_INFORMATION 15 +#define FILE_MODE_INFORMATION 16 +#define FILE_ALIGNMENT_INFORMATION 17 +#define FILE_ALL_INFORMATION 18 +#define FILE_ALLOCATION_INFORMATION 19 +#define FILE_END_OF_FILE_INFORMATION 20 +#define FILE_ALTERNATE_NAME_INFORMATION 21 +#define FILE_STREAM_INFORMATION 22 +#define FILE_PIPE_INFORMATION 23 +#define FILE_PIPE_LOCAL_INFORMATION 24 +#define FILE_PIPE_REMOTE_INFORMATION 25 +#define FILE_MAILSLOT_QUERY_INFORMATION 26 +#define FILE_MAILSLOT_SET_INFORMATION 27 +#define FILE_COMPRESSION_INFORMATION 28 +#define FILE_OBJECT_ID_INFORMATION 29 +/* Number 30 not defined in documents */ +#define FILE_MOVE_CLUSTER_INFORMATION 31 +#define FILE_QUOTA_INFORMATION 32 +#define FILE_REPARSE_POINT_INFORMATION 33 +#define FILE_NETWORK_OPEN_INFORMATION 34 +#define FILE_ATTRIBUTE_TAG_INFORMATION 35 +#define FILE_TRACKING_INFORMATION 36 +#define FILEID_BOTH_DIRECTORY_INFORMATION 37 +#define FILEID_FULL_DIRECTORY_INFORMATION 38 +#define FILE_VALID_DATA_LENGTH_INFORMATION 39 +#define FILE_SHORT_NAME_INFORMATION 40 +#define FILE_SFIO_RESERVE_INFORMATION 44 +#define FILE_SFIO_VOLUME_INFORMATION 45 +#define FILE_HARD_LINK_INFORMATION 46 +#define FILE_NORMALIZED_NAME_INFORMATION 48 +#define FILEID_GLOBAL_TX_DIRECTORY_INFORMATION 50 +#define FILE_STANDARD_LINK_INFORMATION 54 + +#define OP_BREAK_STRUCT_SIZE_20 24 +#define OP_BREAK_STRUCT_SIZE_21 36 + +struct smb2_file_access_info { + __le32 AccessFlags; +} __packed; + +struct smb2_file_alignment_info { + __le32 AlignmentRequirement; +} __packed; + +struct smb2_file_internal_info { + __le64 IndexNumber; +} __packed; /* level 6 Query */ + +struct smb2_file_rename_info { /* encoding of request for level 10 */ + __u8 ReplaceIfExists; /* 1 = replace existing target with new */ + /* 0 = fail if target already exists */ + __u8 Reserved[7]; + __u64 RootDirectory; /* MBZ for network operations (why says spec?) */ + __le32 FileNameLength; + char FileName[0]; /* New name to be assigned */ +} __packed; /* level 10 Set */ + +struct smb2_file_link_info { /* encoding of request for level 11 */ + __u8 ReplaceIfExists; /* 1 = replace existing link with new */ + /* 0 = fail if link already exists */ + __u8 Reserved[7]; + __u64 RootDirectory; /* MBZ for network operations (why says spec?) */ + __le32 FileNameLength; + char FileName[0]; /* Name to be assigned to new link */ +} __packed; /* level 11 Set */ + +/* + * This level 18, although with struct with same name is different from cifs + * level 0x107. Level 0x107 has an extra u64 between AccessFlags and + * CurrentByteOffset. + */ +struct smb2_file_all_info { /* data block encoding of response to level 18 */ + __le64 CreationTime; /* Beginning of FILE_BASIC_INFO equivalent */ + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 Attributes; + __u32 Pad1; /* End of FILE_BASIC_INFO_INFO equivalent */ + __le64 AllocationSize; /* Beginning of FILE_STANDARD_INFO equivalent */ + __le64 EndOfFile; /* size ie offset to first free byte in file */ + __le32 NumberOfLinks; /* hard links */ + __u8 DeletePending; + __u8 Directory; + __u16 Pad2; /* End of FILE_STANDARD_INFO equivalent */ + __le64 IndexNumber; + __le32 EASize; + __le32 AccessFlags; + __le64 CurrentByteOffset; + __le32 Mode; + __le32 AlignmentRequirement; + __le32 FileNameLength; + char FileName[1]; +} __packed; /* level 18 Query */ + +struct smb2_file_basic_info { /* data block encoding of response to level 18 */ + __le64 CreationTime; /* Beginning of FILE_BASIC_INFO equivalent */ + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le32 Attributes; + __u32 Pad1; /* End of FILE_BASIC_INFO_INFO equivalent */ +} __packed; + +struct smb2_file_alt_name_info { + __le32 FileNameLength; + char FileName[0]; +} __packed; + +struct smb2_file_stream_info { + __le32 NextEntryOffset; + __le32 StreamNameLength; + __le64 StreamSize; + __le64 StreamAllocationSize; + char StreamName[0]; +} __packed; + +struct smb2_file_eof_info { /* encoding of request for level 10 */ + __le64 EndOfFile; /* new end of file value */ +} __packed; /* level 20 Set */ + +struct smb2_file_ntwrk_info { + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 AllocationSize; + __le64 EndOfFile; + __le32 Attributes; + __le32 Reserved; +} __packed; + +struct smb2_file_standard_info { + __le64 AllocationSize; + __le64 EndOfFile; + __le32 NumberOfLinks; /* hard links */ + __u8 DeletePending; + __u8 Directory; + __le16 Reserved; +} __packed; /* level 18 Query */ + +struct smb2_file_ea_info { + __le32 EASize; +} __packed; + +struct smb2_file_alloc_info { + __le64 AllocationSize; +} __packed; + +struct smb2_file_disposition_info { + __u8 DeletePending; +} __packed; + +struct smb2_file_pos_info { + __le64 CurrentByteOffset; +} __packed; + +#define FILE_MODE_INFO_MASK cpu_to_le32(0x0000103e) + +struct smb2_file_mode_info { + __le32 Mode; +} __packed; + +#define COMPRESSION_FORMAT_NONE 0x0000 +#define COMPRESSION_FORMAT_LZNT1 0x0002 + +struct smb2_file_comp_info { + __le64 CompressedFileSize; + __le16 CompressionFormat; + __u8 CompressionUnitShift; + __u8 ChunkShift; + __u8 ClusterShift; + __u8 Reserved[3]; +} __packed; + +struct smb2_file_attr_tag_info { + __le32 FileAttributes; + __le32 ReparseTag; +} __packed; + +#define SL_RESTART_SCAN 0x00000001 +#define SL_RETURN_SINGLE_ENTRY 0x00000002 +#define SL_INDEX_SPECIFIED 0x00000004 + +struct smb2_ea_info_req { + __le32 NextEntryOffset; + __u8 EaNameLength; + char name[1]; +} __packed; /* level 15 Query */ + +struct smb2_ea_info { + __le32 NextEntryOffset; + __u8 Flags; + __u8 EaNameLength; + __le16 EaValueLength; + char name[1]; + /* optionally followed by value */ +} __packed; /* level 15 Query */ + +struct create_ea_buf_req { + struct create_context ccontext; + __u8 Name[8]; + struct smb2_ea_info ea; +} __packed; + +struct create_sd_buf_req { + struct create_context ccontext; + __u8 Name[8]; + struct smb_ntsd ntsd; +} __packed; + +/* Find File infolevels */ +#define SMB_FIND_FILE_POSIX_INFO 0x064 + +/* Level 100 query info */ +struct smb311_posix_qinfo { + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 DosAttributes; + __le64 Inode; + __le32 DeviceId; + __le32 Zero; + /* beginning of POSIX Create Context Response */ + __le32 HardLinks; + __le32 ReparseTag; + __le32 Mode; + u8 Sids[]; + /* + * var sized owner SID + * var sized group SID + * le32 filenamelength + * u8 filename[] + */ +} __packed; + +struct smb2_posix_info { + __le32 NextEntryOffset; + __u32 Ignored; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 DosAttributes; + __le64 Inode; + __le32 DeviceId; + __le32 Zero; + /* beginning of POSIX Create Context Response */ + __le32 HardLinks; + __le32 ReparseTag; + __le32 Mode; + /* SidBuffer contain two sids (UNIX user sid(16), UNIX group sid(16)) */ + u8 SidBuffer[32]; + __le32 name_len; + u8 name[]; + /* + * var sized owner SID + * var sized group SID + * le32 filenamelength + * u8 filename[] + */ +} __packed; + +/* functions */ +int init_smb2_0_server(struct ksmbd_conn *conn); +void init_smb2_1_server(struct ksmbd_conn *conn); +void init_smb3_0_server(struct ksmbd_conn *conn); +void init_smb3_02_server(struct ksmbd_conn *conn); +int init_smb3_11_server(struct ksmbd_conn *conn); + +void init_smb2_max_read_size(unsigned int sz); +void init_smb2_max_write_size(unsigned int sz); +void init_smb2_max_trans_size(unsigned int sz); +void init_smb2_max_credits(unsigned int sz); + +bool is_smb2_neg_cmd(struct ksmbd_work *work); +bool is_smb2_rsp(struct ksmbd_work *work); + +u16 get_smb2_cmd_val(struct ksmbd_work *work); +void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err); +int init_smb2_rsp_hdr(struct ksmbd_work *work); +int smb2_allocate_rsp_buf(struct ksmbd_work *work); +bool is_chained_smb2_message(struct ksmbd_work *work); +int init_smb2_neg_rsp(struct ksmbd_work *work); +void smb2_set_err_rsp(struct ksmbd_work *work); +int smb2_check_user_session(struct ksmbd_work *work); +int smb2_get_ksmbd_tcon(struct ksmbd_work *work); +bool smb2_is_sign_req(struct ksmbd_work *work, unsigned int command); +int smb2_check_sign_req(struct ksmbd_work *work); +void smb2_set_sign_rsp(struct ksmbd_work *work); +int smb3_check_sign_req(struct ksmbd_work *work); +void smb3_set_sign_rsp(struct ksmbd_work *work); +int find_matching_smb2_dialect(int start_index, __le16 *cli_dialects, + __le16 dialects_count); +struct file_lock *smb_flock_init(struct file *f); +int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), + void **arg); +void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status); +struct channel *lookup_chann_list(struct ksmbd_session *sess, + struct ksmbd_conn *conn); +void smb3_preauth_hash_rsp(struct ksmbd_work *work); +bool smb3_is_transform_hdr(void *buf); +int smb3_decrypt_req(struct ksmbd_work *work); +int smb3_encrypt_resp(struct ksmbd_work *work); +bool smb3_11_final_sess_setup_resp(struct ksmbd_work *work); +int smb2_set_rsp_credits(struct ksmbd_work *work); +bool smb3_encryption_negotiated(struct ksmbd_conn *conn); + +/* smb2 misc functions */ +int ksmbd_smb2_check_message(struct ksmbd_work *work); + +/* smb2 command handlers */ +int smb2_handle_negotiate(struct ksmbd_work *work); +int smb2_negotiate_request(struct ksmbd_work *work); +int smb2_sess_setup(struct ksmbd_work *work); +int smb2_tree_connect(struct ksmbd_work *work); +int smb2_tree_disconnect(struct ksmbd_work *work); +int smb2_session_logoff(struct ksmbd_work *work); +int smb2_open(struct ksmbd_work *work); +int smb2_query_info(struct ksmbd_work *work); +int smb2_query_dir(struct ksmbd_work *work); +int smb2_close(struct ksmbd_work *work); +int smb2_echo(struct ksmbd_work *work); +int smb2_set_info(struct ksmbd_work *work); +int smb2_read(struct ksmbd_work *work); +int smb2_write(struct ksmbd_work *work); +int smb2_flush(struct ksmbd_work *work); +int smb2_cancel(struct ksmbd_work *work); +int smb2_lock(struct ksmbd_work *work); +int smb2_ioctl(struct ksmbd_work *work); +int smb2_oplock_break(struct ksmbd_work *work); +int smb2_notify(struct ksmbd_work *ksmbd_work); + +/* + * Get the body of the smb2 message excluding the 4 byte rfc1002 headers + * from request/response buffer. + */ +static inline void *smb2_get_msg(void *buf) +{ + return buf + 4; +} + +#endif /* _SMB2PDU_H */ diff --git a/fs/ksmbd/smb_common.c b/fs/ksmbd/smb_common.c new file mode 100644 index 0000000000000..df2fbe4763e44 --- /dev/null +++ b/fs/ksmbd/smb_common.c @@ -0,0 +1,746 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + * Copyright (C) 2018 Namjae Jeon + */ + +#include + +#include "smb_common.h" +#ifdef CONFIG_SMB_INSECURE_SERVER +#include "smb1pdu.h" +#endif +#include "server.h" +#include "misc.h" +#include "smbstatus.h" +#include "connection.h" +#include "ksmbd_work.h" +#include "mgmt/user_session.h" +#include "mgmt/user_config.h" +#include "mgmt/tree_connect.h" +#include "mgmt/share_config.h" + +/*for shortname implementation */ +static const char basechars[43] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-!@#$%"; +#define MANGLE_BASE (sizeof(basechars) / sizeof(char) - 1) +#define MAGIC_CHAR '~' +#define PERIOD '.' +#define mangle(V) ((char)(basechars[(V) % MANGLE_BASE])) + +#ifdef CONFIG_SMB_INSECURE_SERVER +#define KSMBD_MIN_SUPPORTED_HEADER_SIZE (sizeof(struct smb_hdr)) +#else +#define KSMBD_MIN_SUPPORTED_HEADER_SIZE (sizeof(struct smb2_hdr)) +#endif + +struct smb_protocol { + int index; + char *name; + char *prot; + __u16 prot_id; +}; + +static struct smb_protocol smb1_protos[] = { +#ifdef CONFIG_SMB_INSECURE_SERVER + { + SMB1_PROT, + "\2NT LM 0.12", + "NT1", + SMB10_PROT_ID + }, + { + SMB2_PROT, + "\2SMB 2.002", + "SMB2_02", + SMB20_PROT_ID + }, +#endif + { + SMB2X_PROT, + "\2SMB 2.???", + "SMB2_22", + SMB2X_PROT_ID + }, +}; + +static struct smb_protocol smb2_protos[] = { + { + SMB2_PROT, + "\2SMB 2.002", + "SMB2_02", + SMB20_PROT_ID + }, + { + SMB21_PROT, + "\2SMB 2.1", + "SMB2_10", + SMB21_PROT_ID + }, + { + SMB30_PROT, + "\2SMB 3.0", + "SMB3_00", + SMB30_PROT_ID + }, + { + SMB302_PROT, + "\2SMB 3.02", + "SMB3_02", + SMB302_PROT_ID + }, + { + SMB311_PROT, + "\2SMB 3.1.1", + "SMB3_11", + SMB311_PROT_ID + }, +}; + +unsigned int ksmbd_server_side_copy_max_chunk_count(void) +{ + return 256; +} + +unsigned int ksmbd_server_side_copy_max_chunk_size(void) +{ + return (2U << 30) - 1; +} + +unsigned int ksmbd_server_side_copy_max_total_size(void) +{ + return (2U << 30) - 1; +} + +inline int ksmbd_min_protocol(void) +{ +#ifdef CONFIG_SMB_INSECURE_SERVER + return SMB1_PROT; +#else + return SMB2_PROT; +#endif +} + +inline int ksmbd_max_protocol(void) +{ + return SMB311_PROT; +} + +int ksmbd_lookup_protocol_idx(char *str) +{ + int offt = ARRAY_SIZE(smb1_protos) - 1; + int len = strlen(str); + + while (offt >= 0) { + if (!strncmp(str, smb1_protos[offt].prot, len)) { + ksmbd_debug(SMB, "selected %s dialect idx = %d\n", + smb1_protos[offt].prot, offt); + return smb1_protos[offt].index; + } + offt--; + } + + offt = ARRAY_SIZE(smb2_protos) - 1; + while (offt >= 0) { + if (!strncmp(str, smb2_protos[offt].prot, len)) { + ksmbd_debug(SMB, "selected %s dialect idx = %d\n", + smb2_protos[offt].prot, offt); + return smb2_protos[offt].index; + } + offt--; + } + return -1; +} + +/** + * ksmbd_verify_smb_message() - check for valid smb2 request header + * @work: smb work + * + * check for valid smb signature and packet direction(request/response) + * + * Return: 0 on success, otherwise error + */ +int ksmbd_verify_smb_message(struct ksmbd_work *work) +{ + struct smb2_hdr *smb2_hdr = ksmbd_req_buf_next(work); + +#ifdef CONFIG_SMB_INSECURE_SERVER + if (smb2_hdr->ProtocolId == SMB2_PROTO_NUMBER) { + ksmbd_debug(SMB, "got SMB2 command\n"); + return ksmbd_smb2_check_message(work); + } + + work->conn->outstanding_credits++; + return ksmbd_smb1_check_message(work); +#else + struct smb_hdr *hdr; + + if (smb2_hdr->ProtocolId == SMB2_PROTO_NUMBER) + return ksmbd_smb2_check_message(work); + + hdr = work->request_buf; + if (*(__le32 *)hdr->Protocol == SMB1_PROTO_NUMBER && + hdr->Command == SMB_COM_NEGOTIATE) { + work->conn->outstanding_credits++; + return 0; + } + + return -EINVAL; +#endif +} + +/** + * ksmbd_smb_request() - check for valid smb request type + * @conn: connection instance + * + * Return: true on success, otherwise false + */ +bool ksmbd_smb_request(struct ksmbd_conn *conn) +{ + return conn->request_buf[0] == 0; +} + +static bool supported_protocol(int idx) +{ + if (idx == SMB2X_PROT && + (server_conf.min_protocol >= SMB21_PROT || + server_conf.max_protocol <= SMB311_PROT)) + return true; + + return (server_conf.min_protocol <= idx && + idx <= server_conf.max_protocol); +} + +static char *next_dialect(char *dialect, int *next_off, int bcount) +{ + dialect = dialect + *next_off; + *next_off = strnlen(dialect, bcount); + if (dialect[*next_off] != '\0') + return NULL; + return dialect; +} + +static int ksmbd_lookup_dialect_by_name(char *cli_dialects, __le16 byte_count) +{ + int i, seq_num, bcount, next; + char *dialect; + + for (i = ARRAY_SIZE(smb1_protos) - 1; i >= 0; i--) { + seq_num = 0; + next = 0; + dialect = cli_dialects; + bcount = le16_to_cpu(byte_count); + do { + dialect = next_dialect(dialect, &next, bcount); + if (!dialect) + break; + ksmbd_debug(SMB, "client requested dialect %s\n", + dialect); + if (!strcmp(dialect, smb1_protos[i].name)) { + if (supported_protocol(smb1_protos[i].index)) { + ksmbd_debug(SMB, + "selected %s dialect\n", + smb1_protos[i].name); + if (smb1_protos[i].index == SMB1_PROT) + return seq_num; + return smb1_protos[i].prot_id; + } + } + seq_num++; + bcount -= (++next); + } while (bcount > 0); + } + + return BAD_PROT_ID; +} + +int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count) +{ + int i; + int count; + + for (i = ARRAY_SIZE(smb2_protos) - 1; i >= 0; i--) { + count = le16_to_cpu(dialects_count); + while (--count >= 0) { + ksmbd_debug(SMB, "client requested dialect 0x%x\n", + le16_to_cpu(cli_dialects[count])); + if (le16_to_cpu(cli_dialects[count]) != + smb2_protos[i].prot_id) + continue; + + if (supported_protocol(smb2_protos[i].index)) { + ksmbd_debug(SMB, "selected %s dialect\n", + smb2_protos[i].name); + return smb2_protos[i].prot_id; + } + } + } + + return BAD_PROT_ID; +} + +static int ksmbd_negotiate_smb_dialect(void *buf) +{ + int smb_buf_length = get_rfc1002_len(buf); + __le32 proto = ((struct smb2_hdr *)smb2_get_msg(buf))->ProtocolId; + + if (proto == SMB2_PROTO_NUMBER) { + struct smb2_negotiate_req *req; + int smb2_neg_size = + offsetof(struct smb2_negotiate_req, Dialects); + + req = (struct smb2_negotiate_req *)smb2_get_msg(buf); + if (smb2_neg_size > smb_buf_length) + goto err_out; + + if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) > + smb_buf_length) + goto err_out; + + return ksmbd_lookup_dialect_by_id(req->Dialects, + req->DialectCount); + } + + proto = *(__le32 *)((struct smb_hdr *)buf)->Protocol; + if (proto == SMB1_PROTO_NUMBER) { + struct smb_negotiate_req *req; + + req = (struct smb_negotiate_req *)buf; + if (le16_to_cpu(req->ByteCount) < 2) + goto err_out; + + if (offsetof(struct smb_negotiate_req, DialectsArray) - 4 + + le16_to_cpu(req->ByteCount) > smb_buf_length) { + goto err_out; + } + + return ksmbd_lookup_dialect_by_name(req->DialectsArray, + req->ByteCount); + } + +err_out: + return BAD_PROT_ID; +} + +#define SMB_COM_NEGOTIATE 0x72 +int ksmbd_init_smb_server(struct ksmbd_work *work) +{ + struct ksmbd_conn *conn = work->conn; +#ifdef CONFIG_SMB_INSECURE_SERVER + void *buf = work->request_buf; + __le32 proto; +#endif + + if (conn->need_neg == false) + return 0; + +#ifdef CONFIG_SMB_INSECURE_SERVER + proto = *(__le32 *)((struct smb_hdr *)buf)->Protocol; + if (proto == SMB1_PROTO_NUMBER) + init_smb1_server(conn); + else + init_smb3_11_server(conn); +#else + init_smb3_11_server(conn); +#endif + + if (conn->ops->get_cmd_val(work) != SMB_COM_NEGOTIATE) + conn->need_neg = false; + return 0; +} + +bool ksmbd_pdu_size_has_room(unsigned int pdu) +{ + return (pdu >= KSMBD_MIN_SUPPORTED_HEADER_SIZE - 4); +} + +int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level, + struct ksmbd_file *dir, + struct ksmbd_dir_info *d_info, + char *search_pattern, + int (*fn)(struct ksmbd_conn *, int, + struct ksmbd_dir_info *, + struct ksmbd_kstat *)) +{ + int i, rc = 0; + struct ksmbd_conn *conn = work->conn; + struct user_namespace *user_ns = file_mnt_user_ns(dir->filp); + + for (i = 0; i < 2; i++) { + struct kstat kstat; + struct ksmbd_kstat ksmbd_kstat; + struct dentry *dentry; + + if (!dir->dot_dotdot[i]) { /* fill dot entry info */ + if (i == 0) { + d_info->name = "."; + d_info->name_len = 1; + dentry = dir->filp->f_path.dentry; + } else { + d_info->name = ".."; + d_info->name_len = 2; + dentry = dir->filp->f_path.dentry->d_parent; + } + + if (!match_pattern(d_info->name, d_info->name_len, + search_pattern)) { + dir->dot_dotdot[i] = 1; + continue; + } + + ksmbd_kstat.kstat = &kstat; + ksmbd_vfs_fill_dentry_attrs(work, + user_ns, + dentry, + &ksmbd_kstat); + rc = fn(conn, info_level, d_info, &ksmbd_kstat); + if (rc) + break; + if (d_info->out_buf_len <= 0) + break; + + dir->dot_dotdot[i] = 1; + if (d_info->flags & SMB2_RETURN_SINGLE_ENTRY) { + d_info->out_buf_len = 0; + break; + } + } + } + + return rc; +} + +/** + * ksmbd_extract_shortname() - get shortname from long filename + * @conn: connection instance + * @longname: source long filename + * @shortname: destination short filename + * + * Return: shortname length or 0 when source long name is '.' or '..' + * TODO: Though this function comforms the restriction of 8.3 Filename spec, + * but the result is different with Windows 7's one. need to check. + */ +int ksmbd_extract_shortname(struct ksmbd_conn *conn, const char *longname, + char *shortname) +{ + const char *p; + char base[9], extension[4]; + char out[13] = {0}; + int baselen = 0; + int extlen = 0, len = 0; + unsigned int csum = 0; + const unsigned char *ptr; + bool dot_present = true; + + p = longname; + if ((*p == '.') || (!(strcmp(p, "..")))) { + /*no mangling required */ + return 0; + } + + p = strrchr(longname, '.'); + if (p == longname) { /*name starts with a dot*/ + strscpy(extension, "___", strlen("___")); + } else { + if (p) { + p++; + while (*p && extlen < 3) { + if (*p != '.') + extension[extlen++] = toupper(*p); + p++; + } + extension[extlen] = '\0'; + } else { + dot_present = false; + } + } + + p = longname; + if (*p == '.') { + p++; + longname++; + } + while (*p && (baselen < 5)) { + if (*p != '.') + base[baselen++] = toupper(*p); + p++; + } + + base[baselen] = MAGIC_CHAR; + memcpy(out, base, baselen + 1); + + ptr = longname; + len = strlen(longname); + for (; len > 0; len--, ptr++) + csum += *ptr; + + csum = csum % (MANGLE_BASE * MANGLE_BASE); + out[baselen + 1] = mangle(csum / MANGLE_BASE); + out[baselen + 2] = mangle(csum); + out[baselen + 3] = PERIOD; + + if (dot_present) + memcpy(&out[baselen + 4], extension, 4); + else + out[baselen + 4] = '\0'; + smbConvertToUTF16((__le16 *)shortname, out, PATH_MAX, + conn->local_nls, 0); + len = strlen(out) * 2; + return len; +} + +static int __smb2_negotiate(struct ksmbd_conn *conn) +{ + return (conn->dialect >= SMB20_PROT_ID && + conn->dialect <= SMB311_PROT_ID); +} + +#ifndef CONFIG_SMB_INSECURE_SERVER +static int smb_handle_negotiate(struct ksmbd_work *work) +{ + struct smb_negotiate_rsp *neg_rsp = work->response_buf; + + ksmbd_debug(SMB, "Unsupported SMB protocol\n"); + neg_rsp->hdr.Status.CifsError = STATUS_INVALID_LOGON_TYPE; + return -EINVAL; +} +#endif + +int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command) +{ + struct ksmbd_conn *conn = work->conn; + int ret; + + conn->dialect = + ksmbd_negotiate_smb_dialect(work->request_buf); + ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); + + if (command == SMB2_NEGOTIATE_HE) { + struct smb2_hdr *smb2_hdr = smb2_get_msg(work->request_buf); + + if (smb2_hdr->ProtocolId != SMB2_PROTO_NUMBER) { + ksmbd_debug(SMB, "Downgrade to SMB1 negotiation\n"); + command = SMB_COM_NEGOTIATE; + } + } + + if (command == SMB2_NEGOTIATE_HE) { + ret = smb2_handle_negotiate(work); + init_smb2_neg_rsp(work); + return ret; + } + + if (command == SMB_COM_NEGOTIATE) { + if (__smb2_negotiate(conn)) { + conn->need_neg = true; + init_smb3_11_server(conn); + init_smb2_neg_rsp(work); + ksmbd_debug(SMB, "Upgrade to SMB2 negotiation\n"); + return 0; + } + return smb_handle_negotiate(work); + } + + pr_err("Unknown SMB negotiation command: %u\n", command); + return -EINVAL; +} + +enum SHARED_MODE_ERRORS { + SHARE_DELETE_ERROR, + SHARE_READ_ERROR, + SHARE_WRITE_ERROR, + FILE_READ_ERROR, + FILE_WRITE_ERROR, + FILE_DELETE_ERROR, +}; + +static const char * const shared_mode_errors[] = { + "Current access mode does not permit SHARE_DELETE", + "Current access mode does not permit SHARE_READ", + "Current access mode does not permit SHARE_WRITE", + "Desired access mode does not permit FILE_READ", + "Desired access mode does not permit FILE_WRITE", + "Desired access mode does not permit FILE_DELETE", +}; + +static void smb_shared_mode_error(int error, struct ksmbd_file *prev_fp, + struct ksmbd_file *curr_fp) +{ + ksmbd_debug(SMB, "%s\n", shared_mode_errors[error]); + ksmbd_debug(SMB, "Current mode: 0x%x Desired mode: 0x%x\n", + prev_fp->saccess, curr_fp->daccess); +} + +int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp) +{ + int rc = 0; + struct ksmbd_file *prev_fp; + + /* + * Lookup fp in master fp list, and check desired access and + * shared mode between previous open and current open. + */ + read_lock(&curr_fp->f_ci->m_lock); + list_for_each_entry(prev_fp, &curr_fp->f_ci->m_fp_list, node) { + if (file_inode(filp) != file_inode(prev_fp->filp)) + continue; + + if (filp == prev_fp->filp) + continue; + + if (ksmbd_stream_fd(prev_fp) && ksmbd_stream_fd(curr_fp)) + if (strcmp(prev_fp->stream.name, curr_fp->stream.name)) + continue; + + if (prev_fp->attrib_only != curr_fp->attrib_only) + continue; + + if (!(prev_fp->saccess & FILE_SHARE_DELETE_LE) && + curr_fp->daccess & FILE_DELETE_LE) { + smb_shared_mode_error(SHARE_DELETE_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + + /* + * Only check FILE_SHARE_DELETE if stream opened and + * normal file opened. + */ + if (ksmbd_stream_fd(prev_fp) && !ksmbd_stream_fd(curr_fp)) + continue; + + if (!(prev_fp->saccess & FILE_SHARE_READ_LE) && + curr_fp->daccess & (FILE_EXECUTE_LE | FILE_READ_DATA_LE)) { + smb_shared_mode_error(SHARE_READ_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + + if (!(prev_fp->saccess & FILE_SHARE_WRITE_LE) && + curr_fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE)) { + smb_shared_mode_error(SHARE_WRITE_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + + if (prev_fp->daccess & (FILE_EXECUTE_LE | FILE_READ_DATA_LE) && + !(curr_fp->saccess & FILE_SHARE_READ_LE)) { + smb_shared_mode_error(FILE_READ_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + + if (prev_fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE) && + !(curr_fp->saccess & FILE_SHARE_WRITE_LE)) { + smb_shared_mode_error(FILE_WRITE_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + + if (prev_fp->daccess & FILE_DELETE_LE && + !(curr_fp->saccess & FILE_SHARE_DELETE_LE)) { + smb_shared_mode_error(FILE_DELETE_ERROR, + prev_fp, + curr_fp); + rc = -EPERM; + break; + } + } + read_unlock(&curr_fp->f_ci->m_lock); + + return rc; +} + +bool is_asterisk(char *p) +{ + return p && p[0] == '*'; +} + +int ksmbd_override_fsids(struct ksmbd_work *work) +{ + struct ksmbd_session *sess = work->sess; + struct ksmbd_share_config *share = work->tcon->share_conf; + struct cred *cred; + struct group_info *gi; + unsigned int uid; + unsigned int gid; + + uid = user_uid(sess->user); + gid = user_gid(sess->user); + if (share->force_uid != KSMBD_SHARE_INVALID_UID) + uid = share->force_uid; + if (share->force_gid != KSMBD_SHARE_INVALID_GID) + gid = share->force_gid; + + cred = prepare_kernel_cred(&init_task); + if (!cred) + return -ENOMEM; + + cred->fsuid = make_kuid(&init_user_ns, uid); + cred->fsgid = make_kgid(&init_user_ns, gid); + + gi = groups_alloc(0); + if (!gi) { + abort_creds(cred); + return -ENOMEM; + } + set_groups(cred, gi); + put_group_info(gi); + + if (!uid_eq(cred->fsuid, GLOBAL_ROOT_UID)) + cred->cap_effective = cap_drop_fs_set(cred->cap_effective); + + WARN_ON(work->saved_cred); + work->saved_cred = override_creds(cred); + if (!work->saved_cred) { + abort_creds(cred); + return -EINVAL; + } + return 0; +} + +void ksmbd_revert_fsids(struct ksmbd_work *work) +{ + const struct cred *cred; + + WARN_ON(!work->saved_cred); + + cred = current_cred(); + revert_creds(work->saved_cred); + put_cred(cred); + work->saved_cred = NULL; +} + +__le32 smb_map_generic_desired_access(__le32 daccess) +{ + if (daccess & FILE_GENERIC_READ_LE) { + daccess |= cpu_to_le32(GENERIC_READ_FLAGS); + daccess &= ~FILE_GENERIC_READ_LE; + } + + if (daccess & FILE_GENERIC_WRITE_LE) { + daccess |= cpu_to_le32(GENERIC_WRITE_FLAGS); + daccess &= ~FILE_GENERIC_WRITE_LE; + } + + if (daccess & FILE_GENERIC_EXECUTE_LE) { + daccess |= cpu_to_le32(GENERIC_EXECUTE_FLAGS); + daccess &= ~FILE_GENERIC_EXECUTE_LE; + } + + if (daccess & FILE_GENERIC_ALL_LE) { + daccess |= cpu_to_le32(GENERIC_ALL_FLAGS); + daccess &= ~FILE_GENERIC_ALL_LE; + } + + return daccess; +} diff --git a/fs/ksmbd/smb_common.h b/fs/ksmbd/smb_common.h new file mode 100644 index 0000000000000..fe5dfc856aae2 --- /dev/null +++ b/fs/ksmbd/smb_common.h @@ -0,0 +1,534 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __SMB_COMMON_H__ +#define __SMB_COMMON_H__ + +#include + +#include "glob.h" +#include "nterr.h" +#include "smb2pdu.h" + +/* ksmbd's Specific ERRNO */ +#define ESHARE 50000 + +#define SMB1_PROT 0 +#define SMB2_PROT 1 +#define SMB21_PROT 2 +/* multi-protocol negotiate request */ +#define SMB2X_PROT 3 +#define SMB30_PROT 4 +#define SMB302_PROT 5 +#define SMB311_PROT 6 +#define BAD_PROT 0xFFFF + +#define SMB1_VERSION_STRING "1.0" +#define SMB20_VERSION_STRING "2.0" +#define SMB21_VERSION_STRING "2.1" +#define SMB30_VERSION_STRING "3.0" +#define SMB302_VERSION_STRING "3.02" +#define SMB311_VERSION_STRING "3.1.1" + +/* Dialects */ +#define SMB10_PROT_ID 0x00 +#define SMB20_PROT_ID 0x0202 +#define SMB21_PROT_ID 0x0210 +/* multi-protocol negotiate request */ +#define SMB2X_PROT_ID 0x02FF +#define SMB30_PROT_ID 0x0300 +#define SMB302_PROT_ID 0x0302 +#define SMB311_PROT_ID 0x0311 +#define BAD_PROT_ID 0xFFFF + +#define SMB_ECHO_INTERVAL (60 * HZ) + +#define CIFS_DEFAULT_IOSIZE (64 * 1024) +#define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */ + +#define MAX_STREAM_PROT_LEN 0x00FFFFFF + +#define IS_SMB2(x) ((x)->vals->protocol_id != SMB10_PROT_ID) +#define MAX_HEADER_SIZE(conn) ((conn)->vals->max_header_size) + +/* Responses when opening a file. */ +#define F_SUPERSEDED 0 +#define F_OPENED 1 +#define F_CREATED 2 +#define F_OVERWRITTEN 3 + +/* + * File Attribute flags + */ +#define ATTR_READONLY 0x0001 +#define ATTR_HIDDEN 0x0002 +#define ATTR_SYSTEM 0x0004 +#define ATTR_VOLUME 0x0008 +#define ATTR_DIRECTORY 0x0010 +#define ATTR_ARCHIVE 0x0020 +#define ATTR_DEVICE 0x0040 +#define ATTR_NORMAL 0x0080 +#define ATTR_TEMPORARY 0x0100 +#define ATTR_SPARSE 0x0200 +#define ATTR_REPARSE 0x0400 +#define ATTR_COMPRESSED 0x0800 +#define ATTR_OFFLINE 0x1000 +#define ATTR_NOT_CONTENT_INDEXED 0x2000 +#define ATTR_ENCRYPTED 0x4000 +#define ATTR_POSIX_SEMANTICS 0x01000000 +#define ATTR_BACKUP_SEMANTICS 0x02000000 +#define ATTR_DELETE_ON_CLOSE 0x04000000 +#define ATTR_SEQUENTIAL_SCAN 0x08000000 +#define ATTR_RANDOM_ACCESS 0x10000000 +#define ATTR_NO_BUFFERING 0x20000000 +#define ATTR_WRITE_THROUGH 0x80000000 + +#define ATTR_READONLY_LE cpu_to_le32(ATTR_READONLY) +#define ATTR_HIDDEN_LE cpu_to_le32(ATTR_HIDDEN) +#define ATTR_SYSTEM_LE cpu_to_le32(ATTR_SYSTEM) +#define ATTR_DIRECTORY_LE cpu_to_le32(ATTR_DIRECTORY) +#define ATTR_ARCHIVE_LE cpu_to_le32(ATTR_ARCHIVE) +#define ATTR_NORMAL_LE cpu_to_le32(ATTR_NORMAL) +#define ATTR_TEMPORARY_LE cpu_to_le32(ATTR_TEMPORARY) +#define ATTR_SPARSE_FILE_LE cpu_to_le32(ATTR_SPARSE) +#define ATTR_REPARSE_POINT_LE cpu_to_le32(ATTR_REPARSE) +#define ATTR_COMPRESSED_LE cpu_to_le32(ATTR_COMPRESSED) +#define ATTR_OFFLINE_LE cpu_to_le32(ATTR_OFFLINE) +#define ATTR_NOT_CONTENT_INDEXED_LE cpu_to_le32(ATTR_NOT_CONTENT_INDEXED) +#define ATTR_ENCRYPTED_LE cpu_to_le32(ATTR_ENCRYPTED) +#define ATTR_INTEGRITY_STREAML_LE cpu_to_le32(0x00008000) +#define ATTR_NO_SCRUB_DATA_LE cpu_to_le32(0x00020000) +#define ATTR_MASK_LE cpu_to_le32(0x00007FB7) + +/* List of FileSystemAttributes - see 2.5.1 of MS-FSCC */ +#define FILE_SUPPORTS_SPARSE_VDL 0x10000000 /* faster nonsparse extend */ +#define FILE_SUPPORTS_BLOCK_REFCOUNTING 0x08000000 /* allow ioctl dup extents */ +#define FILE_SUPPORT_INTEGRITY_STREAMS 0x04000000 +#define FILE_SUPPORTS_USN_JOURNAL 0x02000000 +#define FILE_SUPPORTS_OPEN_BY_FILE_ID 0x01000000 +#define FILE_SUPPORTS_EXTENDED_ATTRIBUTES 0x00800000 +#define FILE_SUPPORTS_HARD_LINKS 0x00400000 +#define FILE_SUPPORTS_TRANSACTIONS 0x00200000 +#define FILE_SEQUENTIAL_WRITE_ONCE 0x00100000 +#define FILE_READ_ONLY_VOLUME 0x00080000 +#define FILE_NAMED_STREAMS 0x00040000 +#define FILE_SUPPORTS_ENCRYPTION 0x00020000 +#define FILE_SUPPORTS_OBJECT_IDS 0x00010000 +#define FILE_VOLUME_IS_COMPRESSED 0x00008000 +#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100 +#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080 +#define FILE_SUPPORTS_SPARSE_FILES 0x00000040 +#define FILE_VOLUME_QUOTAS 0x00000020 +#define FILE_FILE_COMPRESSION 0x00000010 +#define FILE_PERSISTENT_ACLS 0x00000008 +#define FILE_UNICODE_ON_DISK 0x00000004 +#define FILE_CASE_PRESERVED_NAMES 0x00000002 +#define FILE_CASE_SENSITIVE_SEARCH 0x00000001 + +#define FILE_READ_DATA 0x00000001 /* Data can be read from the file */ +#define FILE_WRITE_DATA 0x00000002 /* Data can be written to the file */ +#define FILE_APPEND_DATA 0x00000004 /* Data can be appended to the file */ +#define FILE_READ_EA 0x00000008 /* Extended attributes associated */ +/* with the file can be read */ +#define FILE_WRITE_EA 0x00000010 /* Extended attributes associated */ +/* with the file can be written */ +#define FILE_EXECUTE 0x00000020 /*Data can be read into memory from */ +/* the file using system paging I/O */ +#define FILE_DELETE_CHILD 0x00000040 +#define FILE_READ_ATTRIBUTES 0x00000080 /* Attributes associated with the */ +/* file can be read */ +#define FILE_WRITE_ATTRIBUTES 0x00000100 /* Attributes associated with the */ +/* file can be written */ +#define DELETE 0x00010000 /* The file can be deleted */ +#define READ_CONTROL 0x00020000 /* The access control list and */ +/* ownership associated with the */ +/* file can be read */ +#define WRITE_DAC 0x00040000 /* The access control list and */ +/* ownership associated with the */ +/* file can be written. */ +#define WRITE_OWNER 0x00080000 /* Ownership information associated */ +/* with the file can be written */ +#define SYNCHRONIZE 0x00100000 /* The file handle can waited on to */ +/* synchronize with the completion */ +/* of an input/output request */ +#define GENERIC_ALL 0x10000000 +#define GENERIC_EXECUTE 0x20000000 +#define GENERIC_WRITE 0x40000000 +#define GENERIC_READ 0x80000000 +/* In summary - Relevant file */ +/* access flags from CIFS are */ +/* file_read_data, file_write_data */ +/* file_execute, file_read_attributes*/ +/* write_dac, and delete. */ + +#define FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES) +#define FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \ + | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES) +#define FILE_EXEC_RIGHTS (FILE_EXECUTE) + +#define SET_FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA \ + | FILE_READ_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) +#define SET_FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \ + | FILE_WRITE_EA \ + | FILE_DELETE_CHILD \ + | FILE_WRITE_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) +#define SET_FILE_EXEC_RIGHTS (FILE_READ_EA | FILE_WRITE_EA | FILE_EXECUTE \ + | FILE_READ_ATTRIBUTES \ + | FILE_WRITE_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) + +#define SET_MINIMUM_RIGHTS (FILE_READ_EA | FILE_READ_ATTRIBUTES \ + | READ_CONTROL | SYNCHRONIZE) + +/* generic flags for file open */ +#define GENERIC_READ_FLAGS (READ_CONTROL | FILE_READ_DATA | \ + FILE_READ_ATTRIBUTES | \ + FILE_READ_EA | SYNCHRONIZE) + +#define GENERIC_WRITE_FLAGS (READ_CONTROL | FILE_WRITE_DATA | \ + FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | \ + FILE_APPEND_DATA | SYNCHRONIZE) + +#define GENERIC_EXECUTE_FLAGS (READ_CONTROL | FILE_EXECUTE | \ + FILE_READ_ATTRIBUTES | SYNCHRONIZE) + +#define GENERIC_ALL_FLAGS (DELETE | READ_CONTROL | WRITE_DAC | \ + WRITE_OWNER | SYNCHRONIZE | FILE_READ_DATA | \ + FILE_WRITE_DATA | FILE_APPEND_DATA | \ + FILE_READ_EA | FILE_WRITE_EA | \ + FILE_EXECUTE | FILE_DELETE_CHILD | \ + FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES) + +#define SMB1_PROTO_NUMBER cpu_to_le32(0x424d53ff) +#define SMB_COM_NEGOTIATE 0x72 + +#define SMB1_CLIENT_GUID_SIZE (16) +struct smb_hdr { + __be32 smb_buf_length; + __u8 Protocol[4]; + __u8 Command; + union { + struct { + __u8 ErrorClass; + __u8 Reserved; + __le16 Error; + } __packed DosError; + __le32 CifsError; + } __packed Status; + __u8 Flags; + __le16 Flags2; /* note: le */ + __le16 PidHigh; + union { + struct { + __le32 SequenceNumber; /* le */ + __u32 Reserved; /* zero */ + } __packed Sequence; + __u8 SecuritySignature[8]; /* le */ + } __packed Signature; + __u8 pad[2]; + __le16 Tid; + __le16 Pid; + __le16 Uid; + __le16 Mid; + __u8 WordCount; +} __packed; + +struct smb_negotiate_req { + struct smb_hdr hdr; /* wct = 0 */ + __le16 ByteCount; + unsigned char DialectsArray[1]; +} __packed; + +struct smb_negotiate_rsp { + struct smb_hdr hdr; /* wct = 17 */ + __le16 DialectIndex; /* 0xFFFF = no dialect acceptable */ + __u8 SecurityMode; + __le16 MaxMpxCount; + __le16 MaxNumberVcs; + __le32 MaxBufferSize; + __le32 MaxRawSize; + __le32 SessionKey; + __le32 Capabilities; /* see below */ + __le32 SystemTimeLow; + __le32 SystemTimeHigh; + __le16 ServerTimeZone; + __u8 EncryptionKeyLength; + __le16 ByteCount; + union { + unsigned char EncryptionKey[8]; /* cap extended security off */ + /* followed by Domain name - if extended security is off */ + /* followed by 16 bytes of server GUID */ + /* then security blob if cap_extended_security negotiated */ + struct { + unsigned char GUID[SMB1_CLIENT_GUID_SIZE]; + unsigned char SecurityBlob[1]; + } __packed extended_response; + } __packed u; +} __packed; + +struct filesystem_attribute_info { + __le32 Attributes; + __le32 MaxPathNameComponentLength; + __le32 FileSystemNameLen; + __le16 FileSystemName[1]; /* do not have to save this - get subset? */ +} __packed; + +struct filesystem_device_info { + __le32 DeviceType; + __le32 DeviceCharacteristics; +} __packed; /* device info level 0x104 */ + +struct filesystem_vol_info { + __le64 VolumeCreationTime; + __le32 SerialNumber; + __le32 VolumeLabelSize; + __le16 Reserved; + __le16 VolumeLabel[1]; +} __packed; + +struct filesystem_info { + __le64 TotalAllocationUnits; + __le64 FreeAllocationUnits; + __le32 SectorsPerAllocationUnit; + __le32 BytesPerSector; +} __packed; /* size info, level 0x103 */ + +#define EXTENDED_INFO_MAGIC 0x43667364 /* Cfsd */ +#define STRING_LENGTH 28 + +struct fs_extended_info { + __le32 magic; + __le32 version; + __le32 release; + __u64 rel_date; + char version_string[STRING_LENGTH]; +} __packed; + +struct object_id_info { + char objid[16]; + struct fs_extended_info extended_info; +} __packed; + +struct file_directory_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + char FileName[]; +} __packed; /* level 0x101 FF resp data */ + +struct file_names_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le32 FileNameLength; + char FileName[]; +} __packed; /* level 0xc FF resp data */ + +struct file_full_directory_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; + char FileName[]; +} __packed; /* level 0x102 FF resp */ + +struct file_both_directory_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; /* length of the xattrs */ + __u8 ShortNameLength; + __u8 Reserved; + __u8 ShortName[24]; + char FileName[]; +} __packed; /* level 0x104 FFrsp data */ + +struct file_id_both_directory_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; /* length of the xattrs */ + __u8 ShortNameLength; + __u8 Reserved; + __u8 ShortName[24]; + __le16 Reserved2; + __le64 UniqueId; + char FileName[]; +} __packed; + +struct file_id_full_dir_info { + __le32 NextEntryOffset; + __u32 FileIndex; + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; + __le64 ChangeTime; + __le64 EndOfFile; + __le64 AllocationSize; + __le32 ExtFileAttributes; + __le32 FileNameLength; + __le32 EaSize; /* EA size */ + __le32 Reserved; + __le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/ + char FileName[]; +} __packed; /* level 0x105 FF rsp data */ + +struct smb_version_values { + char *version_string; + __u16 protocol_id; + __le16 lock_cmd; + __u32 capabilities; + __u32 max_read_size; + __u32 max_write_size; + __u32 max_trans_size; + __u32 max_credits; + __u32 large_lock_type; + __u32 exclusive_lock_type; + __u32 shared_lock_type; + __u32 unlock_lock_type; + size_t header_size; + size_t max_header_size; + size_t read_rsp_size; + unsigned int cap_unix; + unsigned int cap_nt_find; + unsigned int cap_large_files; + __u16 signing_enabled; + __u16 signing_required; + size_t create_lease_size; + size_t create_durable_size; + size_t create_durable_v2_size; + size_t create_mxac_size; + size_t create_disk_id_size; + size_t create_posix_size; +}; + +struct filesystem_posix_info { + /* For undefined recommended transfer size return -1 in that field */ + __le32 OptimalTransferSize; /* bsize on some os, iosize on other os */ + __le32 BlockSize; + /* The next three fields are in terms of the block size. + * (above). If block size is unknown, 4096 would be a + * reasonable block size for a server to report. + * Note that returning the blocks/blocksavail removes need + * to make a second call (to QFSInfo level 0x103 to get this info. + * UserBlockAvail is typically less than or equal to BlocksAvail, + * if no distinction is made return the same value in each + */ + __le64 TotalBlocks; + __le64 BlocksAvail; /* bfree */ + __le64 UserBlocksAvail; /* bavail */ + /* For undefined Node fields or FSID return -1 */ + __le64 TotalFileNodes; + __le64 FreeFileNodes; + __le64 FileSysIdentifier; /* fsid */ + /* NB Namelen comes from FILE_SYSTEM_ATTRIBUTE_INFO call */ + /* NB flags can come from FILE_SYSTEM_DEVICE_INFO call */ +} __packed; + +struct smb_version_ops { + u16 (*get_cmd_val)(struct ksmbd_work *swork); + int (*init_rsp_hdr)(struct ksmbd_work *swork); + void (*set_rsp_status)(struct ksmbd_work *swork, __le32 err); + int (*allocate_rsp_buf)(struct ksmbd_work *work); + int (*set_rsp_credits)(struct ksmbd_work *work); + int (*check_user_session)(struct ksmbd_work *work); + int (*get_ksmbd_tcon)(struct ksmbd_work *work); + bool (*is_sign_req)(struct ksmbd_work *work, unsigned int command); + int (*check_sign_req)(struct ksmbd_work *work); + void (*set_sign_rsp)(struct ksmbd_work *work); + int (*generate_signingkey)(struct ksmbd_session *sess, struct ksmbd_conn *conn); + int (*generate_encryptionkey)(struct ksmbd_conn *conn, struct ksmbd_session *sess); + bool (*is_transform_hdr)(void *buf); + int (*decrypt_req)(struct ksmbd_work *work); + int (*encrypt_resp)(struct ksmbd_work *work); +}; + +struct smb_version_cmds { + int (*proc)(struct ksmbd_work *swork); +}; + +int ksmbd_min_protocol(void); +int ksmbd_max_protocol(void); + +int ksmbd_lookup_protocol_idx(char *str); + +int ksmbd_verify_smb_message(struct ksmbd_work *work); +bool ksmbd_smb_request(struct ksmbd_conn *conn); + +int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count); + +int ksmbd_init_smb_server(struct ksmbd_work *work); + +bool ksmbd_pdu_size_has_room(unsigned int pdu); + +struct ksmbd_kstat; +int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, + int info_level, + struct ksmbd_file *dir, + struct ksmbd_dir_info *d_info, + char *search_pattern, + int (*fn)(struct ksmbd_conn *, + int, + struct ksmbd_dir_info *, + struct ksmbd_kstat *)); + +int ksmbd_extract_shortname(struct ksmbd_conn *conn, + const char *longname, + char *shortname); + +int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command); + +int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp); +int ksmbd_override_fsids(struct ksmbd_work *work); +void ksmbd_revert_fsids(struct ksmbd_work *work); + +unsigned int ksmbd_server_side_copy_max_chunk_count(void); +unsigned int ksmbd_server_side_copy_max_chunk_size(void); +unsigned int ksmbd_server_side_copy_max_total_size(void); +bool is_asterisk(char *p); +__le32 smb_map_generic_desired_access(__le32 daccess); + +static inline unsigned int get_rfc1002_len(void *buf) +{ + return be32_to_cpu(*((__be32 *)buf)) & 0xffffff; +} + +static inline void inc_rfc1001_len(void *buf, int count) +{ + be32_add_cpu((__be32 *)buf, count); +} +#endif /* __SMB_COMMON_H__ */ diff --git a/fs/ksmbd/smbacl.c b/fs/ksmbd/smbacl.c new file mode 100644 index 0000000000000..3d7f5a09e26ad --- /dev/null +++ b/fs/ksmbd/smbacl.c @@ -0,0 +1,1505 @@ +// SPDX-License-Identifier: LGPL-2.1+ +/* + * Copyright (C) International Business Machines Corp., 2007,2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * Copyright (C) 2020 Samsung Electronics Co., Ltd. + * Author(s): Namjae Jeon + */ + +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0) +#include +#endif + +#include "smbacl.h" +#include "smb_common.h" +#include "server.h" +#include "misc.h" +#include "mgmt/share_config.h" + +static const struct smb_sid domain = {1, 4, {0, 0, 0, 0, 0, 5}, + {cpu_to_le32(21), cpu_to_le32(1), cpu_to_le32(2), cpu_to_le32(3), + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* security id for everyone/world system group */ +static const struct smb_sid creator_owner = { + 1, 1, {0, 0, 0, 0, 0, 3}, {0} }; +/* security id for everyone/world system group */ +static const struct smb_sid creator_group = { + 1, 1, {0, 0, 0, 0, 0, 3}, {cpu_to_le32(1)} }; + +/* security id for everyone/world system group */ +static const struct smb_sid sid_everyone = { + 1, 1, {0, 0, 0, 0, 0, 1}, {0} }; +/* security id for Authenticated Users system group */ +static const struct smb_sid sid_authusers = { + 1, 1, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(11)} }; + +/* S-1-22-1 Unmapped Unix users */ +static const struct smb_sid sid_unix_users = {1, 1, {0, 0, 0, 0, 0, 22}, + {cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* S-1-22-2 Unmapped Unix groups */ +static const struct smb_sid sid_unix_groups = { 1, 1, {0, 0, 0, 0, 0, 22}, + {cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* + * See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx + */ + +/* S-1-5-88 MS NFS and Apple style UID/GID/mode */ + +/* S-1-5-88-1 Unix uid */ +static const struct smb_sid sid_unix_NFS_users = { 1, 2, {0, 0, 0, 0, 0, 5}, + {cpu_to_le32(88), + cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* S-1-5-88-2 Unix gid */ +static const struct smb_sid sid_unix_NFS_groups = { 1, 2, {0, 0, 0, 0, 0, 5}, + {cpu_to_le32(88), + cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* S-1-5-88-3 Unix mode */ +static const struct smb_sid sid_unix_NFS_mode = { 1, 2, {0, 0, 0, 0, 0, 5}, + {cpu_to_le32(88), + cpu_to_le32(3), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; + +/* + * if the two SIDs (roughly equivalent to a UUID for a user or group) are + * the same returns zero, if they do not match returns non-zero. + */ +int compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid) +{ + int i; + int num_subauth, num_sat, num_saw; + + if (!ctsid || !cwsid) + return 1; + + /* compare the revision */ + if (ctsid->revision != cwsid->revision) { + if (ctsid->revision > cwsid->revision) + return 1; + else + return -1; + } + + /* compare all of the six auth values */ + for (i = 0; i < NUM_AUTHS; ++i) { + if (ctsid->authority[i] != cwsid->authority[i]) { + if (ctsid->authority[i] > cwsid->authority[i]) + return 1; + else + return -1; + } + } + + /* compare all of the subauth values if any */ + num_sat = ctsid->num_subauth; + num_saw = cwsid->num_subauth; + num_subauth = num_sat < num_saw ? num_sat : num_saw; + if (num_subauth) { + for (i = 0; i < num_subauth; ++i) { + if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) { + if (le32_to_cpu(ctsid->sub_auth[i]) > + le32_to_cpu(cwsid->sub_auth[i])) + return 1; + else + return -1; + } + } + } + + return 0; /* sids compare/match */ +} + +static void smb_copy_sid(struct smb_sid *dst, const struct smb_sid *src) +{ + int i; + + dst->revision = src->revision; + dst->num_subauth = min_t(u8, src->num_subauth, SID_MAX_SUB_AUTHORITIES); + for (i = 0; i < NUM_AUTHS; ++i) + dst->authority[i] = src->authority[i]; + for (i = 0; i < dst->num_subauth; ++i) + dst->sub_auth[i] = src->sub_auth[i]; +} + +/* + * change posix mode to reflect permissions + * pmode is the existing mode (we only want to overwrite part of this + * bits to set can be: S_IRWXU, S_IRWXG or S_IRWXO ie 00700 or 00070 or 00007 + */ +static umode_t access_flags_to_mode(struct smb_fattr *fattr, __le32 ace_flags, + int type) +{ + __u32 flags = le32_to_cpu(ace_flags); + umode_t mode = 0; + + if (flags & GENERIC_ALL) { + mode = 0777; + ksmbd_debug(SMB, "all perms\n"); + return mode; + } + + if ((flags & GENERIC_READ) || (flags & FILE_READ_RIGHTS)) + mode = 0444; + if ((flags & GENERIC_WRITE) || (flags & FILE_WRITE_RIGHTS)) { + mode |= 0222; + if (S_ISDIR(fattr->cf_mode)) + mode |= 0111; + } + if ((flags & GENERIC_EXECUTE) || (flags & FILE_EXEC_RIGHTS)) + mode |= 0111; + + if (type == ACCESS_DENIED_ACE_TYPE || type == ACCESS_DENIED_OBJECT_ACE_TYPE) + mode = ~mode; + + ksmbd_debug(SMB, "access flags 0x%x mode now %04o\n", flags, mode); + + return mode; +} + +/* + * Generate access flags to reflect permissions mode is the existing mode. + * This function is called for every ACE in the DACL whose SID matches + * with either owner or group or everyone. + */ +static void mode_to_access_flags(umode_t mode, umode_t bits_to_use, + __u32 *pace_flags) +{ + /* reset access mask */ + *pace_flags = 0x0; + + /* bits to use are either S_IRWXU or S_IRWXG or S_IRWXO */ + mode &= bits_to_use; + + /* + * check for R/W/X UGO since we do not know whose flags + * is this but we have cleared all the bits sans RWX for + * either user or group or other as per bits_to_use + */ + if (mode & 0444) + *pace_flags |= SET_FILE_READ_RIGHTS; + if (mode & 0222) + *pace_flags |= FILE_WRITE_RIGHTS; + if (mode & 0111) + *pace_flags |= SET_FILE_EXEC_RIGHTS; + + ksmbd_debug(SMB, "mode: %o, access flags now 0x%x\n", + mode, *pace_flags); +} + +static __u16 fill_ace_for_sid(struct smb_ace *pntace, + const struct smb_sid *psid, int type, int flags, + umode_t mode, umode_t bits) +{ + int i; + __u16 size = 0; + __u32 access_req = 0; + + pntace->type = type; + pntace->flags = flags; + mode_to_access_flags(mode, bits, &access_req); + if (!access_req) + access_req = SET_MINIMUM_RIGHTS; + pntace->access_req = cpu_to_le32(access_req); + + pntace->sid.revision = psid->revision; + pntace->sid.num_subauth = psid->num_subauth; + for (i = 0; i < NUM_AUTHS; i++) + pntace->sid.authority[i] = psid->authority[i]; + for (i = 0; i < psid->num_subauth; i++) + pntace->sid.sub_auth[i] = psid->sub_auth[i]; + + size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth * 4); + pntace->size = cpu_to_le16(size); + + return size; +} + +void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid) +{ + switch (sidtype) { + case SIDOWNER: + smb_copy_sid(ssid, &server_conf.domain_sid); + break; + case SIDUNIX_USER: + smb_copy_sid(ssid, &sid_unix_users); + break; + case SIDUNIX_GROUP: + smb_copy_sid(ssid, &sid_unix_groups); + break; + case SIDCREATOR_OWNER: + smb_copy_sid(ssid, &creator_owner); + return; + case SIDCREATOR_GROUP: + smb_copy_sid(ssid, &creator_group); + return; + case SIDNFS_USER: + smb_copy_sid(ssid, &sid_unix_NFS_users); + break; + case SIDNFS_GROUP: + smb_copy_sid(ssid, &sid_unix_NFS_groups); + break; + case SIDNFS_MODE: + smb_copy_sid(ssid, &sid_unix_NFS_mode); + break; + default: + return; + } + + /* RID */ + ssid->sub_auth[ssid->num_subauth] = cpu_to_le32(cid); + ssid->num_subauth++; +} + +static int sid_to_id(struct user_namespace *user_ns, + struct smb_sid *psid, uint sidtype, + struct smb_fattr *fattr) +{ + int rc = -EINVAL; + + /* + * If we have too many subauthorities, then something is really wrong. + * Just return an error. + */ + if (unlikely(psid->num_subauth > SID_MAX_SUB_AUTHORITIES)) { + pr_err("%s: %u subauthorities is too many!\n", + __func__, psid->num_subauth); + return -EIO; + } + + if (!compare_sids(psid, &sid_everyone)) + return -EIO; + + if (sidtype == SIDOWNER) { + kuid_t uid; + uid_t id; + + id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 52) && LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0)) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + uid = KUIDT_INIT(id); + uid = from_vfsuid(user_ns, &init_user_ns, VFSUIDT_INIT(uid)); +#else + uid = mapped_kuid_user(user_ns, &init_user_ns, KUIDT_INIT(id)); +#endif +#else + /* + * Translate raw sid into kuid in the server's user + * namespace. + */ + uid = make_kuid(&init_user_ns, id); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + /* If this is an idmapped mount, apply the idmapping. */ + uid = kuid_from_mnt(user_ns, uid); +#endif +#endif + if (uid_valid(uid)) { + fattr->cf_uid = uid; + rc = 0; + } + } else { + kgid_t gid; + gid_t id; + + id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 52) && LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0)) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + gid = KGIDT_INIT(id); + gid = from_vfsgid(user_ns, &init_user_ns, VFSGIDT_INIT(gid)); +#else + gid = mapped_kgid_user(user_ns, &init_user_ns, KGIDT_INIT(id)); +#endif +#else + /* + * Translate raw sid into kgid in the server's user + * namespace. + */ + gid = make_kgid(&init_user_ns, id); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + /* If this is an idmapped mount, apply the idmapping. */ + gid = kgid_from_mnt(user_ns, gid); +#endif +#endif + if (gid_valid(gid)) { + fattr->cf_gid = gid; + rc = 0; + } + } + + return rc; +} + +void posix_state_to_acl(struct posix_acl_state *state, + struct posix_acl_entry *pace) +{ + int i; + + pace->e_tag = ACL_USER_OBJ; + pace->e_perm = state->owner.allow; + for (i = 0; i < state->users->n; i++) { + pace++; + pace->e_tag = ACL_USER; + pace->e_uid = state->users->aces[i].uid; + pace->e_perm = state->users->aces[i].perms.allow; + } + + pace++; + pace->e_tag = ACL_GROUP_OBJ; + pace->e_perm = state->group.allow; + + for (i = 0; i < state->groups->n; i++) { + pace++; + pace->e_tag = ACL_GROUP; + pace->e_gid = state->groups->aces[i].gid; + pace->e_perm = state->groups->aces[i].perms.allow; + } + + if (state->users->n || state->groups->n) { + pace++; + pace->e_tag = ACL_MASK; + pace->e_perm = state->mask.allow; + } + + pace++; + pace->e_tag = ACL_OTHER; + pace->e_perm = state->other.allow; +} + +int init_acl_state(struct posix_acl_state *state, int cnt) +{ + int alloc; + + memset(state, 0, sizeof(struct posix_acl_state)); + /* + * In the worst case, each individual acl could be for a distinct + * named user or group, but we don't know which, so we allocate + * enough space for either: + */ + alloc = sizeof(struct posix_ace_state_array) + + cnt * sizeof(struct posix_user_ace_state); + state->users = kzalloc(alloc, GFP_KERNEL); + if (!state->users) + return -ENOMEM; + state->groups = kzalloc(alloc, GFP_KERNEL); + if (!state->groups) { + kfree(state->users); + return -ENOMEM; + } + return 0; +} + +void free_acl_state(struct posix_acl_state *state) +{ + kfree(state->users); + kfree(state->groups); +} + +static void parse_dacl(struct user_namespace *user_ns, + struct smb_acl *pdacl, char *end_of_acl, + struct smb_sid *pownersid, struct smb_sid *pgrpsid, + struct smb_fattr *fattr) +{ + int i, ret; + int num_aces = 0; + unsigned int acl_size; + char *acl_base; + struct smb_ace **ppace; + struct posix_acl_entry *cf_pace, *cf_pdace; + struct posix_acl_state acl_state, default_acl_state; + umode_t mode = 0, acl_mode; + bool owner_found = false, group_found = false, others_found = false; + + if (!pdacl) + return; + + /* validate that we do not go past end of acl */ + if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl) || + end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) { + pr_err("ACL too small to parse DACL\n"); + return; + } + + ksmbd_debug(SMB, "DACL revision %d size %d num aces %d\n", + le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size), + le32_to_cpu(pdacl->num_aces)); + + acl_base = (char *)pdacl; + acl_size = sizeof(struct smb_acl); + + num_aces = le32_to_cpu(pdacl->num_aces); + if (num_aces <= 0) + return; + + if (num_aces > ULONG_MAX / sizeof(struct smb_ace *)) + return; + + ppace = kmalloc_array(num_aces, sizeof(struct smb_ace *), GFP_KERNEL); + if (!ppace) + return; + + ret = init_acl_state(&acl_state, num_aces); + if (ret) + return; + ret = init_acl_state(&default_acl_state, num_aces); + if (ret) { + free_acl_state(&acl_state); + return; + } + + /* + * reset rwx permissions for user/group/other. + * Also, if num_aces is 0 i.e. DACL has no ACEs, + * user/group/other have no permissions + */ + for (i = 0; i < num_aces; ++i) { + if (end_of_acl - acl_base < acl_size) + break; + + ppace[i] = (struct smb_ace *)(acl_base + acl_size); + acl_base = (char *)ppace[i]; + acl_size = offsetof(struct smb_ace, sid) + + offsetof(struct smb_sid, sub_auth); + + if (end_of_acl - acl_base < acl_size || + ppace[i]->sid.num_subauth > SID_MAX_SUB_AUTHORITIES || + (end_of_acl - acl_base < + acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth) || + (le16_to_cpu(ppace[i]->size) < + acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth)) + break; + + acl_size = le16_to_cpu(ppace[i]->size); + ppace[i]->access_req = + smb_map_generic_desired_access(ppace[i]->access_req); + + if (!(compare_sids(&ppace[i]->sid, &sid_unix_NFS_mode))) { + fattr->cf_mode = + le32_to_cpu(ppace[i]->sid.sub_auth[2]); + break; + } else if (!compare_sids(&ppace[i]->sid, pownersid)) { + acl_mode = access_flags_to_mode(fattr, + ppace[i]->access_req, + ppace[i]->type); + acl_mode &= 0700; + + if (!owner_found) { + mode &= ~(0700); + mode |= acl_mode; + } + owner_found = true; + } else if (!compare_sids(&ppace[i]->sid, pgrpsid) || + ppace[i]->sid.sub_auth[ppace[i]->sid.num_subauth - 1] == + DOMAIN_USER_RID_LE) { + acl_mode = access_flags_to_mode(fattr, + ppace[i]->access_req, + ppace[i]->type); + acl_mode &= 0070; + if (!group_found) { + mode &= ~(0070); + mode |= acl_mode; + } + group_found = true; + } else if (!compare_sids(&ppace[i]->sid, &sid_everyone)) { + acl_mode = access_flags_to_mode(fattr, + ppace[i]->access_req, + ppace[i]->type); + acl_mode &= 0007; + if (!others_found) { + mode &= ~(0007); + mode |= acl_mode; + } + others_found = true; + } else if (!compare_sids(&ppace[i]->sid, &creator_owner)) { + continue; + } else if (!compare_sids(&ppace[i]->sid, &creator_group)) { + continue; + } else if (!compare_sids(&ppace[i]->sid, &sid_authusers)) { + continue; + } else { + struct smb_fattr temp_fattr; + + acl_mode = access_flags_to_mode(fattr, ppace[i]->access_req, + ppace[i]->type); + temp_fattr.cf_uid = INVALID_UID; + ret = sid_to_id(user_ns, &ppace[i]->sid, SIDOWNER, &temp_fattr); + if (ret || uid_eq(temp_fattr.cf_uid, INVALID_UID)) { + pr_err("%s: Error %d mapping Owner SID to uid\n", + __func__, ret); + continue; + } + + acl_state.owner.allow = ((acl_mode & 0700) >> 6) | 0004; + acl_state.users->aces[acl_state.users->n].uid = + temp_fattr.cf_uid; + acl_state.users->aces[acl_state.users->n++].perms.allow = + ((acl_mode & 0700) >> 6) | 0004; + default_acl_state.owner.allow = ((acl_mode & 0700) >> 6) | 0004; + default_acl_state.users->aces[default_acl_state.users->n].uid = + temp_fattr.cf_uid; + default_acl_state.users->aces[default_acl_state.users->n++].perms.allow = + ((acl_mode & 0700) >> 6) | 0004; + } + } + kfree(ppace); + + if (owner_found) { + /* The owner must be set to at least read-only. */ + acl_state.owner.allow = ((mode & 0700) >> 6) | 0004; + acl_state.users->aces[acl_state.users->n].uid = fattr->cf_uid; + acl_state.users->aces[acl_state.users->n++].perms.allow = + ((mode & 0700) >> 6) | 0004; + default_acl_state.owner.allow = ((mode & 0700) >> 6) | 0004; + default_acl_state.users->aces[default_acl_state.users->n].uid = + fattr->cf_uid; + default_acl_state.users->aces[default_acl_state.users->n++].perms.allow = + ((mode & 0700) >> 6) | 0004; + } + + if (group_found) { + acl_state.group.allow = (mode & 0070) >> 3; + acl_state.groups->aces[acl_state.groups->n].gid = + fattr->cf_gid; + acl_state.groups->aces[acl_state.groups->n++].perms.allow = + (mode & 0070) >> 3; + default_acl_state.group.allow = (mode & 0070) >> 3; + default_acl_state.groups->aces[default_acl_state.groups->n].gid = + fattr->cf_gid; + default_acl_state.groups->aces[default_acl_state.groups->n++].perms.allow = + (mode & 0070) >> 3; + } + + if (others_found) { + fattr->cf_mode &= ~(0007); + fattr->cf_mode |= mode & 0007; + + acl_state.other.allow = mode & 0007; + default_acl_state.other.allow = mode & 0007; + } + + if (acl_state.users->n || acl_state.groups->n) { + acl_state.mask.allow = 0x07; + + if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { + fattr->cf_acls = + posix_acl_alloc(acl_state.users->n + + acl_state.groups->n + 4, GFP_KERNEL); + if (fattr->cf_acls) { + cf_pace = fattr->cf_acls->a_entries; + posix_state_to_acl(&acl_state, cf_pace); + } + } + } + + if (default_acl_state.users->n || default_acl_state.groups->n) { + default_acl_state.mask.allow = 0x07; + + if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { + fattr->cf_dacls = + posix_acl_alloc(default_acl_state.users->n + + default_acl_state.groups->n + 4, GFP_KERNEL); + if (fattr->cf_dacls) { + cf_pdace = fattr->cf_dacls->a_entries; + posix_state_to_acl(&default_acl_state, cf_pdace); + } + } + } + free_acl_state(&acl_state); + free_acl_state(&default_acl_state); +} + +static void set_posix_acl_entries_dacl(struct user_namespace *user_ns, + struct smb_ace *pndace, + struct smb_fattr *fattr, u32 *num_aces, + u16 *size, u32 nt_aces_num) +{ + struct posix_acl_entry *pace; + struct smb_sid *sid; + struct smb_ace *ntace; + int i, j; + + if (!fattr->cf_acls) + goto posix_default_acl; + + pace = fattr->cf_acls->a_entries; + for (i = 0; i < fattr->cf_acls->a_count; i++, pace++) { + int flags = 0; + + sid = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); + if (!sid) + break; + + if (pace->e_tag == ACL_USER) { + uid_t uid; + unsigned int sid_type = SIDOWNER; + + uid = posix_acl_uid_translate(user_ns, pace); + if (!uid) + sid_type = SIDUNIX_USER; + id_to_sid(uid, sid_type, sid); + } else if (pace->e_tag == ACL_GROUP) { + gid_t gid; + + gid = posix_acl_gid_translate(user_ns, pace); + id_to_sid(gid, SIDUNIX_GROUP, sid); + } else if (pace->e_tag == ACL_OTHER && !nt_aces_num) { + smb_copy_sid(sid, &sid_everyone); + } else { + kfree(sid); + continue; + } + ntace = pndace; + for (j = 0; j < nt_aces_num; j++) { + if (ntace->sid.sub_auth[ntace->sid.num_subauth - 1] == + sid->sub_auth[sid->num_subauth - 1]) + goto pass_same_sid; + ntace = (struct smb_ace *)((char *)ntace + + le16_to_cpu(ntace->size)); + } + + if (S_ISDIR(fattr->cf_mode) && pace->e_tag == ACL_OTHER) + flags = 0x03; + + ntace = (struct smb_ace *)((char *)pndace + *size); + *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, flags, + pace->e_perm, 0777); + (*num_aces)++; + if (pace->e_tag == ACL_USER) + ntace->access_req |= + FILE_DELETE_LE | FILE_DELETE_CHILD_LE; + + if (S_ISDIR(fattr->cf_mode) && + (pace->e_tag == ACL_USER || pace->e_tag == ACL_GROUP)) { + ntace = (struct smb_ace *)((char *)pndace + *size); + *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, + 0x03, pace->e_perm, 0777); + (*num_aces)++; + if (pace->e_tag == ACL_USER) + ntace->access_req |= + FILE_DELETE_LE | FILE_DELETE_CHILD_LE; + } + +pass_same_sid: + kfree(sid); + } + + if (nt_aces_num) + return; + +posix_default_acl: + if (!fattr->cf_dacls) + return; + + pace = fattr->cf_dacls->a_entries; + for (i = 0; i < fattr->cf_dacls->a_count; i++, pace++) { + sid = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); + if (!sid) + break; + + if (pace->e_tag == ACL_USER) { + uid_t uid; + + uid = posix_acl_uid_translate(user_ns, pace); + id_to_sid(uid, SIDCREATOR_OWNER, sid); + } else if (pace->e_tag == ACL_GROUP) { + gid_t gid; + + gid = posix_acl_gid_translate(user_ns, pace); + id_to_sid(gid, SIDCREATOR_GROUP, sid); + } else { + kfree(sid); + continue; + } + + ntace = (struct smb_ace *)((char *)pndace + *size); + *size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, 0x0b, + pace->e_perm, 0777); + (*num_aces)++; + if (pace->e_tag == ACL_USER) + ntace->access_req |= + FILE_DELETE_LE | FILE_DELETE_CHILD_LE; + kfree(sid); + } +} + +static void set_ntacl_dacl(struct user_namespace *user_ns, + struct smb_acl *pndacl, + struct smb_acl *nt_dacl, + unsigned int aces_size, + const struct smb_sid *pownersid, + const struct smb_sid *pgrpsid, + struct smb_fattr *fattr) +{ + struct smb_ace *ntace, *pndace; + int nt_num_aces = le32_to_cpu(nt_dacl->num_aces), num_aces = 0; + unsigned short size = 0; + int i; + + pndace = (struct smb_ace *)((char *)pndacl + sizeof(struct smb_acl)); + if (nt_num_aces) { + ntace = (struct smb_ace *)((char *)nt_dacl + sizeof(struct smb_acl)); + for (i = 0; i < nt_num_aces; i++) { + unsigned short nt_ace_size; + + if (offsetof(struct smb_ace, access_req) > aces_size) + break; + + nt_ace_size = le16_to_cpu(ntace->size); + if (nt_ace_size > aces_size) + break; + + memcpy((char *)pndace + size, ntace, nt_ace_size); + size += nt_ace_size; + aces_size -= nt_ace_size; + ntace = (struct smb_ace *)((char *)ntace + nt_ace_size); + num_aces++; + } + } + + set_posix_acl_entries_dacl(user_ns, pndace, fattr, + &num_aces, &size, nt_num_aces); + pndacl->num_aces = cpu_to_le32(num_aces); + pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size); +} + +static void set_mode_dacl(struct user_namespace *user_ns, + struct smb_acl *pndacl, struct smb_fattr *fattr) +{ + struct smb_ace *pace, *pndace; + u32 num_aces = 0; + u16 size = 0, ace_size = 0; + uid_t uid; + const struct smb_sid *sid; + + pace = pndace = (struct smb_ace *)((char *)pndacl + sizeof(struct smb_acl)); + + if (fattr->cf_acls) { + set_posix_acl_entries_dacl(user_ns, pndace, fattr, + &num_aces, &size, num_aces); + goto out; + } + + /* owner RID */ + uid = from_kuid(&init_user_ns, fattr->cf_uid); + if (uid) + sid = &server_conf.domain_sid; + else + sid = &sid_unix_users; + ace_size = fill_ace_for_sid(pace, sid, ACCESS_ALLOWED, 0, + fattr->cf_mode, 0700); + pace->sid.sub_auth[pace->sid.num_subauth++] = cpu_to_le32(uid); + pace->size = cpu_to_le16(ace_size + 4); + size += le16_to_cpu(pace->size); + pace = (struct smb_ace *)((char *)pndace + size); + + /* Group RID */ + ace_size = fill_ace_for_sid(pace, &sid_unix_groups, + ACCESS_ALLOWED, 0, fattr->cf_mode, 0070); + pace->sid.sub_auth[pace->sid.num_subauth++] = + cpu_to_le32(from_kgid(&init_user_ns, fattr->cf_gid)); + pace->size = cpu_to_le16(ace_size + 4); + size += le16_to_cpu(pace->size); + pace = (struct smb_ace *)((char *)pndace + size); + num_aces = 3; + + if (S_ISDIR(fattr->cf_mode)) { + pace = (struct smb_ace *)((char *)pndace + size); + + /* creator owner */ + size += fill_ace_for_sid(pace, &creator_owner, ACCESS_ALLOWED, + 0x0b, fattr->cf_mode, 0700); + pace = (struct smb_ace *)((char *)pndace + size); + + /* creator group */ + size += fill_ace_for_sid(pace, &creator_group, ACCESS_ALLOWED, + 0x0b, fattr->cf_mode, 0070); + pace = (struct smb_ace *)((char *)pndace + size); + num_aces = 5; + } + + /* other */ + size += fill_ace_for_sid(pace, &sid_everyone, ACCESS_ALLOWED, 0, + fattr->cf_mode, 0007); + +out: + pndacl->num_aces = cpu_to_le32(num_aces); + pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size); +} + +static int parse_sid(struct smb_sid *psid, char *end_of_acl) +{ + /* + * validate that we do not go past end of ACL - sid must be at least 8 + * bytes long (assuming no sub-auths - e.g. the null SID + */ + if (end_of_acl < (char *)psid + 8) { + pr_err("ACL too small to parse SID %p\n", psid); + return -EINVAL; + } + + return 0; +} + +/* Convert CIFS ACL to POSIX form */ +int parse_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd, + int acl_len, struct smb_fattr *fattr) +{ + int rc = 0; + struct smb_sid *owner_sid_ptr, *group_sid_ptr; + struct smb_acl *dacl_ptr; /* no need for SACL ptr */ + char *end_of_acl = ((char *)pntsd) + acl_len; + __u32 dacloffset; + int pntsd_type; + + if (!pntsd) + return -EIO; + + if (acl_len < sizeof(struct smb_ntsd)) + return -EINVAL; + + owner_sid_ptr = (struct smb_sid *)((char *)pntsd + + le32_to_cpu(pntsd->osidoffset)); + group_sid_ptr = (struct smb_sid *)((char *)pntsd + + le32_to_cpu(pntsd->gsidoffset)); + dacloffset = le32_to_cpu(pntsd->dacloffset); + dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset); + ksmbd_debug(SMB, + "revision %d type 0x%x ooffset 0x%x goffset 0x%x sacloffset 0x%x dacloffset 0x%x\n", + pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset), + le32_to_cpu(pntsd->gsidoffset), + le32_to_cpu(pntsd->sacloffset), dacloffset); + + pntsd_type = le16_to_cpu(pntsd->type); + if (!(pntsd_type & DACL_PRESENT)) { + ksmbd_debug(SMB, "DACL_PRESENT in DACL type is not set\n"); + return rc; + } + + pntsd->type = cpu_to_le16(DACL_PRESENT); + + if (pntsd->osidoffset) { + rc = parse_sid(owner_sid_ptr, end_of_acl); + if (rc) { + pr_err("%s: Error %d parsing Owner SID\n", __func__, rc); + return rc; + } + + rc = sid_to_id(user_ns, owner_sid_ptr, SIDOWNER, fattr); + if (rc) { + pr_err("%s: Error %d mapping Owner SID to uid\n", + __func__, rc); + owner_sid_ptr = NULL; + } + } + + if (pntsd->gsidoffset) { + rc = parse_sid(group_sid_ptr, end_of_acl); + if (rc) { + pr_err("%s: Error %d mapping Owner SID to gid\n", + __func__, rc); + return rc; + } + rc = sid_to_id(user_ns, group_sid_ptr, SIDUNIX_GROUP, fattr); + if (rc) { + pr_err("%s: Error %d mapping Group SID to gid\n", + __func__, rc); + group_sid_ptr = NULL; + } + } + + if ((pntsd_type & (DACL_AUTO_INHERITED | DACL_AUTO_INHERIT_REQ)) == + (DACL_AUTO_INHERITED | DACL_AUTO_INHERIT_REQ)) + pntsd->type |= cpu_to_le16(DACL_AUTO_INHERITED); + if (pntsd_type & DACL_PROTECTED) + pntsd->type |= cpu_to_le16(DACL_PROTECTED); + + if (dacloffset) { + parse_dacl(user_ns, dacl_ptr, end_of_acl, + owner_sid_ptr, group_sid_ptr, fattr); + } + + return 0; +} + +/* Convert permission bits from mode to equivalent CIFS ACL */ +int build_sec_desc(struct user_namespace *user_ns, + struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd, + int ppntsd_size, int addition_info, __u32 *secdesclen, + struct smb_fattr *fattr) +{ + int rc = 0; + __u32 offset; + struct smb_sid *owner_sid_ptr, *group_sid_ptr; + struct smb_sid *nowner_sid_ptr, *ngroup_sid_ptr; + struct smb_acl *dacl_ptr = NULL; /* no need for SACL ptr */ + uid_t uid; + gid_t gid; + unsigned int sid_type = SIDOWNER; + + nowner_sid_ptr = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); + if (!nowner_sid_ptr) + return -ENOMEM; + + uid = from_kuid(&init_user_ns, fattr->cf_uid); + if (!uid) + sid_type = SIDUNIX_USER; + id_to_sid(uid, sid_type, nowner_sid_ptr); + + ngroup_sid_ptr = kmalloc(sizeof(struct smb_sid), GFP_KERNEL); + if (!ngroup_sid_ptr) { + kfree(nowner_sid_ptr); + return -ENOMEM; + } + + gid = from_kgid(&init_user_ns, fattr->cf_gid); + id_to_sid(gid, SIDUNIX_GROUP, ngroup_sid_ptr); + + offset = sizeof(struct smb_ntsd); + pntsd->sacloffset = 0; + pntsd->revision = cpu_to_le16(1); + pntsd->type = cpu_to_le16(SELF_RELATIVE); + if (ppntsd) + pntsd->type |= ppntsd->type; + + if (addition_info & OWNER_SECINFO) { + pntsd->osidoffset = cpu_to_le32(offset); + owner_sid_ptr = (struct smb_sid *)((char *)pntsd + offset); + smb_copy_sid(owner_sid_ptr, nowner_sid_ptr); + offset += 1 + 1 + 6 + (nowner_sid_ptr->num_subauth * 4); + } + + if (addition_info & GROUP_SECINFO) { + pntsd->gsidoffset = cpu_to_le32(offset); + group_sid_ptr = (struct smb_sid *)((char *)pntsd + offset); + smb_copy_sid(group_sid_ptr, ngroup_sid_ptr); + offset += 1 + 1 + 6 + (ngroup_sid_ptr->num_subauth * 4); + } + + if (addition_info & DACL_SECINFO) { + pntsd->type |= cpu_to_le16(DACL_PRESENT); + dacl_ptr = (struct smb_acl *)((char *)pntsd + offset); + dacl_ptr->revision = cpu_to_le16(2); + dacl_ptr->size = cpu_to_le16(sizeof(struct smb_acl)); + dacl_ptr->num_aces = 0; + + if (!ppntsd) { + set_mode_dacl(user_ns, dacl_ptr, fattr); + } else { + struct smb_acl *ppdacl_ptr; + unsigned int dacl_offset = le32_to_cpu(ppntsd->dacloffset); + int ppdacl_size, ntacl_size = ppntsd_size - dacl_offset; + + if (!dacl_offset || + (dacl_offset + sizeof(struct smb_acl) > ppntsd_size)) + goto out; + + ppdacl_ptr = (struct smb_acl *)((char *)ppntsd + dacl_offset); + ppdacl_size = le16_to_cpu(ppdacl_ptr->size); + if (ppdacl_size > ntacl_size || + ppdacl_size < sizeof(struct smb_acl)) + goto out; + + set_ntacl_dacl(user_ns, dacl_ptr, ppdacl_ptr, + ntacl_size - sizeof(struct smb_acl), + nowner_sid_ptr, ngroup_sid_ptr, + fattr); + } + pntsd->dacloffset = cpu_to_le32(offset); + offset += le16_to_cpu(dacl_ptr->size); + } + +out: + kfree(nowner_sid_ptr); + kfree(ngroup_sid_ptr); + *secdesclen = offset; + return rc; +} + +static void smb_set_ace(struct smb_ace *ace, const struct smb_sid *sid, u8 type, + u8 flags, __le32 access_req) +{ + ace->type = type; + ace->flags = flags; + ace->access_req = access_req; + smb_copy_sid(&ace->sid, sid); + ace->size = cpu_to_le16(1 + 1 + 2 + 4 + 1 + 1 + 6 + (sid->num_subauth * 4)); +} + +int smb_inherit_dacl(struct ksmbd_conn *conn, + const struct path *path, + unsigned int uid, unsigned int gid) +{ + const struct smb_sid *psid, *creator = NULL; + struct smb_ace *parent_aces, *aces; + struct smb_acl *parent_pdacl; + struct smb_ntsd *parent_pntsd = NULL; + struct smb_sid owner_sid, group_sid; + struct dentry *parent = path->dentry->d_parent; + struct user_namespace *user_ns = mnt_user_ns(path->mnt); + int inherited_flags = 0, flags = 0, i, ace_cnt = 0, nt_size = 0, pdacl_size; + int rc = 0, num_aces, dacloffset, pntsd_type, pntsd_size, acl_len, aces_size; + char *aces_base; + bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode); + + pntsd_size = ksmbd_vfs_get_sd_xattr(conn, user_ns, + parent, &parent_pntsd); + if (pntsd_size <= 0) + return -ENOENT; + dacloffset = le32_to_cpu(parent_pntsd->dacloffset); + if (!dacloffset || (dacloffset + sizeof(struct smb_acl) > pntsd_size)) { + rc = -EINVAL; + goto free_parent_pntsd; + } + + parent_pdacl = (struct smb_acl *)((char *)parent_pntsd + dacloffset); + acl_len = pntsd_size - dacloffset; + num_aces = le32_to_cpu(parent_pdacl->num_aces); + pntsd_type = le16_to_cpu(parent_pntsd->type); + pdacl_size = le16_to_cpu(parent_pdacl->size); + + if (pdacl_size > acl_len || pdacl_size < sizeof(struct smb_acl)) { + rc = -EINVAL; + goto free_parent_pntsd; + } + + aces_base = kmalloc(sizeof(struct smb_ace) * num_aces * 2, GFP_KERNEL); + if (!aces_base) { + rc = -ENOMEM; + goto free_parent_pntsd; + } + + aces = (struct smb_ace *)aces_base; + parent_aces = (struct smb_ace *)((char *)parent_pdacl + + sizeof(struct smb_acl)); + aces_size = acl_len - sizeof(struct smb_acl); + + if (pntsd_type & DACL_AUTO_INHERITED) + inherited_flags = INHERITED_ACE; + + for (i = 0; i < num_aces; i++) { + int pace_size; + + if (offsetof(struct smb_ace, access_req) > aces_size) + break; + + pace_size = le16_to_cpu(parent_aces->size); + if (pace_size > aces_size) + break; + + aces_size -= pace_size; + + flags = parent_aces->flags; + if (!smb_inherit_flags(flags, is_dir)) + goto pass; + if (is_dir) { + flags &= ~(INHERIT_ONLY_ACE | INHERITED_ACE); + if (!(flags & CONTAINER_INHERIT_ACE)) + flags |= INHERIT_ONLY_ACE; + if (flags & NO_PROPAGATE_INHERIT_ACE) + flags = 0; + } else { + flags = 0; + } + + if (!compare_sids(&creator_owner, &parent_aces->sid)) { + creator = &creator_owner; + id_to_sid(uid, SIDOWNER, &owner_sid); + psid = &owner_sid; + } else if (!compare_sids(&creator_group, &parent_aces->sid)) { + creator = &creator_group; + id_to_sid(gid, SIDUNIX_GROUP, &group_sid); + psid = &group_sid; + } else { + creator = NULL; + psid = &parent_aces->sid; + } + + if (is_dir && creator && flags & CONTAINER_INHERIT_ACE) { + smb_set_ace(aces, psid, parent_aces->type, inherited_flags, + parent_aces->access_req); + nt_size += le16_to_cpu(aces->size); + ace_cnt++; + aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size)); + flags |= INHERIT_ONLY_ACE; + psid = creator; + } else if (is_dir && !(parent_aces->flags & NO_PROPAGATE_INHERIT_ACE)) { + psid = &parent_aces->sid; + } + + smb_set_ace(aces, psid, parent_aces->type, flags | inherited_flags, + parent_aces->access_req); + nt_size += le16_to_cpu(aces->size); + aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size)); + ace_cnt++; +pass: + parent_aces = (struct smb_ace *)((char *)parent_aces + pace_size); + } + + if (nt_size > 0) { + struct smb_ntsd *pntsd; + struct smb_acl *pdacl; + struct smb_sid *powner_sid = NULL, *pgroup_sid = NULL; + int powner_sid_size = 0, pgroup_sid_size = 0, pntsd_size; + + if (parent_pntsd->osidoffset) { + powner_sid = (struct smb_sid *)((char *)parent_pntsd + + le32_to_cpu(parent_pntsd->osidoffset)); + powner_sid_size = 1 + 1 + 6 + (powner_sid->num_subauth * 4); + } + if (parent_pntsd->gsidoffset) { + pgroup_sid = (struct smb_sid *)((char *)parent_pntsd + + le32_to_cpu(parent_pntsd->gsidoffset)); + pgroup_sid_size = 1 + 1 + 6 + (pgroup_sid->num_subauth * 4); + } + + pntsd = kzalloc(sizeof(struct smb_ntsd) + powner_sid_size + + pgroup_sid_size + sizeof(struct smb_acl) + + nt_size, GFP_KERNEL); + if (!pntsd) { + rc = -ENOMEM; + goto free_aces_base; + } + + pntsd->revision = cpu_to_le16(1); + pntsd->type = cpu_to_le16(SELF_RELATIVE | DACL_PRESENT); + if (le16_to_cpu(parent_pntsd->type) & DACL_AUTO_INHERITED) + pntsd->type |= cpu_to_le16(DACL_AUTO_INHERITED); + pntsd_size = sizeof(struct smb_ntsd); + pntsd->osidoffset = parent_pntsd->osidoffset; + pntsd->gsidoffset = parent_pntsd->gsidoffset; + pntsd->dacloffset = parent_pntsd->dacloffset; + + if (pntsd->osidoffset) { + struct smb_sid *owner_sid = (struct smb_sid *)((char *)pntsd + + le32_to_cpu(pntsd->osidoffset)); + memcpy(owner_sid, powner_sid, powner_sid_size); + pntsd_size += powner_sid_size; + } + + if (pntsd->gsidoffset) { + struct smb_sid *group_sid = (struct smb_sid *)((char *)pntsd + + le32_to_cpu(pntsd->gsidoffset)); + memcpy(group_sid, pgroup_sid, pgroup_sid_size); + pntsd_size += pgroup_sid_size; + } + + if (pntsd->dacloffset) { + struct smb_ace *pace; + + pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset)); + pdacl->revision = cpu_to_le16(2); + pdacl->size = cpu_to_le16(sizeof(struct smb_acl) + nt_size); + pdacl->num_aces = cpu_to_le32(ace_cnt); + pace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); + memcpy(pace, aces_base, nt_size); + pntsd_size += sizeof(struct smb_acl) + nt_size; + } + + ksmbd_vfs_set_sd_xattr(conn, user_ns, + path->dentry, pntsd, pntsd_size); + kfree(pntsd); + } + +free_aces_base: + kfree(aces_base); +free_parent_pntsd: + kfree(parent_pntsd); + return rc; +} + +bool smb_inherit_flags(int flags, bool is_dir) +{ + if (!is_dir) + return (flags & OBJECT_INHERIT_ACE) != 0; + + if (flags & OBJECT_INHERIT_ACE && !(flags & NO_PROPAGATE_INHERIT_ACE)) + return true; + + if (flags & CONTAINER_INHERIT_ACE) + return true; + return false; +} + +int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, + __le32 *pdaccess, int uid) +{ + struct user_namespace *user_ns = mnt_user_ns(path->mnt); + struct smb_ntsd *pntsd = NULL; + struct smb_acl *pdacl; + struct posix_acl *posix_acls; + int rc = 0, pntsd_size, acl_size, aces_size, pdacl_size, dacl_offset; + struct smb_sid sid; + int granted = le32_to_cpu(*pdaccess & ~FILE_MAXIMAL_ACCESS_LE); + struct smb_ace *ace; + int i, found = 0; + unsigned int access_bits = 0; + struct smb_ace *others_ace = NULL; + struct posix_acl_entry *pa_entry; + unsigned int sid_type = SIDOWNER; + unsigned short ace_size; + + ksmbd_debug(SMB, "check permission using windows acl\n"); + pntsd_size = ksmbd_vfs_get_sd_xattr(conn, user_ns, + path->dentry, &pntsd); + if (pntsd_size <= 0 || !pntsd) + goto err_out; + + dacl_offset = le32_to_cpu(pntsd->dacloffset); + if (!dacl_offset || + (dacl_offset + sizeof(struct smb_acl) > pntsd_size)) + goto err_out; + + pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset)); + acl_size = pntsd_size - dacl_offset; + pdacl_size = le16_to_cpu(pdacl->size); + + if (pdacl_size > acl_size || pdacl_size < sizeof(struct smb_acl)) + goto err_out; + + if (!pdacl->num_aces) { + if (!(pdacl_size - sizeof(struct smb_acl)) && + *pdaccess & ~(FILE_READ_CONTROL_LE | FILE_WRITE_DAC_LE)) { + rc = -EACCES; + goto err_out; + } + goto err_out; + } + + if (*pdaccess & FILE_MAXIMAL_ACCESS_LE) { + granted = READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES | + DELETE; + + ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); + aces_size = acl_size - sizeof(struct smb_acl); + for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) { + if (offsetof(struct smb_ace, access_req) > aces_size) + break; + ace_size = le16_to_cpu(ace->size); + if (ace_size > aces_size) + break; + aces_size -= ace_size; + granted |= le32_to_cpu(ace->access_req); + ace = (struct smb_ace *)((char *)ace + le16_to_cpu(ace->size)); + } + + if (!pdacl->num_aces) + granted = GENERIC_ALL_FLAGS; + } + + if (!uid) + sid_type = SIDUNIX_USER; + id_to_sid(uid, sid_type, &sid); + + ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); + aces_size = acl_size - sizeof(struct smb_acl); + for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) { + if (offsetof(struct smb_ace, access_req) > aces_size) + break; + ace_size = le16_to_cpu(ace->size); + if (ace_size > aces_size) + break; + aces_size -= ace_size; + + if (!compare_sids(&sid, &ace->sid) || + !compare_sids(&sid_unix_NFS_mode, &ace->sid)) { + found = 1; + break; + } + if (!compare_sids(&sid_everyone, &ace->sid)) + others_ace = ace; + + ace = (struct smb_ace *)((char *)ace + le16_to_cpu(ace->size)); + } + + if (*pdaccess & FILE_MAXIMAL_ACCESS_LE && found) { + granted = READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES | + DELETE; + + granted |= le32_to_cpu(ace->access_req); + + if (!pdacl->num_aces) + granted = GENERIC_ALL_FLAGS; + } + + if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 2, 0) + posix_acls = get_inode_acl(d_inode(path->dentry), ACL_TYPE_ACCESS); +#else + posix_acls = get_acl(d_inode(path->dentry), ACL_TYPE_ACCESS); +#endif + if (posix_acls && !found) { + unsigned int id = -1; + + pa_entry = posix_acls->a_entries; + for (i = 0; i < posix_acls->a_count; i++, pa_entry++) { + if (pa_entry->e_tag == ACL_USER) + id = posix_acl_uid_translate(user_ns, pa_entry); + else if (pa_entry->e_tag == ACL_GROUP) + id = posix_acl_gid_translate(user_ns, pa_entry); + else + continue; + + if (id == uid) { + mode_to_access_flags(pa_entry->e_perm, + 0777, + &access_bits); + if (!access_bits) + access_bits = + SET_MINIMUM_RIGHTS; + posix_acl_release(posix_acls); + goto check_access_bits; + } + } + } + if (posix_acls) + posix_acl_release(posix_acls); + } + + if (!found) { + if (others_ace) { + ace = others_ace; + } else { + ksmbd_debug(SMB, "Can't find corresponding sid\n"); + rc = -EACCES; + goto err_out; + } + } + + switch (ace->type) { + case ACCESS_ALLOWED_ACE_TYPE: + access_bits = le32_to_cpu(ace->access_req); + break; + case ACCESS_DENIED_ACE_TYPE: + case ACCESS_DENIED_CALLBACK_ACE_TYPE: + access_bits = le32_to_cpu(~ace->access_req); + break; + } + +check_access_bits: + if (granted & + ~(access_bits | FILE_READ_ATTRIBUTES | READ_CONTROL | WRITE_DAC | DELETE)) { + ksmbd_debug(SMB, "Access denied with winACL, granted : %x, access_req : %x\n", + granted, le32_to_cpu(ace->access_req)); + rc = -EACCES; + goto err_out; + } + + *pdaccess = cpu_to_le32(granted); +err_out: + kfree(pntsd); + return rc; +} + +int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, + const struct path *path, struct smb_ntsd *pntsd, int ntsd_len, + bool type_check) +{ + int rc; + struct smb_fattr fattr = {{0}}; + struct inode *inode = d_inode(path->dentry); + struct user_namespace *user_ns = mnt_user_ns(path->mnt); + struct iattr newattrs; + + fattr.cf_uid = INVALID_UID; + fattr.cf_gid = INVALID_GID; + fattr.cf_mode = inode->i_mode; + + rc = parse_sec_desc(user_ns, pntsd, ntsd_len, &fattr); + if (rc) + goto out; + + newattrs.ia_valid = ATTR_CTIME; + if (!uid_eq(fattr.cf_uid, INVALID_UID)) { + newattrs.ia_valid |= ATTR_UID; + newattrs.ia_uid = fattr.cf_uid; + } + if (!gid_eq(fattr.cf_gid, INVALID_GID)) { + inode->i_gid = fattr.cf_gid; + newattrs.ia_valid |= ATTR_GID; + newattrs.ia_gid = fattr.cf_gid; + } + newattrs.ia_valid |= ATTR_MODE; + newattrs.ia_mode = (inode->i_mode & ~0777) | (fattr.cf_mode & 0777); + + ksmbd_vfs_remove_acl_xattrs(user_ns, path->dentry); + /* Update posix acls */ + if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && fattr.cf_dacls) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 2, 0) + rc = set_posix_acl(user_ns, path->dentry, +#else + rc = set_posix_acl(user_ns, inode, +#endif + ACL_TYPE_ACCESS, + fattr.cf_acls); +#else + rc = set_posix_acl(inode, ACL_TYPE_ACCESS, fattr.cf_acls); +#endif + if (rc < 0) + ksmbd_debug(SMB, + "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", + rc); + if (S_ISDIR(inode->i_mode) && fattr.cf_dacls) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 2, 0) + rc = set_posix_acl(user_ns, path->dentry, +#else + rc = set_posix_acl(user_ns, inode, +#endif + ACL_TYPE_DEFAULT, fattr.cf_dacls); +#else + rc = set_posix_acl(inode, ACL_TYPE_DEFAULT, + fattr.cf_dacls); +#endif + if (rc) + ksmbd_debug(SMB, + "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", + rc); + } + } + + inode_lock(inode); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + rc = notify_change(user_ns, path->dentry, &newattrs, NULL); +#else + rc = notify_change(path->dentry, &newattrs, NULL); +#endif + inode_unlock(inode); + if (rc) + goto out; + + /* Check it only calling from SD BUFFER context */ + if (type_check && !(le16_to_cpu(pntsd->type) & DACL_PRESENT)) + goto out; + + if (test_share_config_flag(tcon->share_conf, KSMBD_SHARE_FLAG_ACL_XATTR)) { + /* Update WinACL in xattr */ + ksmbd_vfs_remove_sd_xattrs(user_ns, path->dentry); + ksmbd_vfs_set_sd_xattr(conn, user_ns, + path->dentry, pntsd, ntsd_len); + } + +out: + posix_acl_release(fattr.cf_acls); + posix_acl_release(fattr.cf_dacls); + mark_inode_dirty(inode); + return rc; +} + +void ksmbd_init_domain(u32 *sub_auth) +{ + int i; + + memcpy(&server_conf.domain_sid, &domain, sizeof(struct smb_sid)); + for (i = 0; i < 3; ++i) + server_conf.domain_sid.sub_auth[i + 1] = cpu_to_le32(sub_auth[i]); +} diff --git a/fs/ksmbd/smbacl.h b/fs/ksmbd/smbacl.h new file mode 100644 index 0000000000000..d153aab64e6bc --- /dev/null +++ b/fs/ksmbd/smbacl.h @@ -0,0 +1,283 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/* + * Copyright (c) International Business Machines Corp., 2007 + * Author(s): Steve French (sfrench@us.ibm.com) + * Modified by Namjae Jeon (linkinjeon@kernel.org) + */ + +#ifndef _SMBACL_H +#define _SMBACL_H + +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0) +#include +#endif + +#include "mgmt/tree_connect.h" + +#define NUM_AUTHS (6) /* number of authority fields */ +#define SID_MAX_SUB_AUTHORITIES (15) /* max number of sub authority fields */ + +/* + * ACE types - see MS-DTYP 2.4.4.1 + */ +enum { + ACCESS_ALLOWED, + ACCESS_DENIED, +}; + +/* + * Security ID types + */ +enum { + SIDOWNER = 1, + SIDGROUP, + SIDCREATOR_OWNER, + SIDCREATOR_GROUP, + SIDUNIX_USER, + SIDUNIX_GROUP, + SIDNFS_USER, + SIDNFS_GROUP, + SIDNFS_MODE, +}; + +/* Revision for ACLs */ +#define SD_REVISION 1 + +/* Control flags for Security Descriptor */ +#define OWNER_DEFAULTED 0x0001 +#define GROUP_DEFAULTED 0x0002 +#define DACL_PRESENT 0x0004 +#define DACL_DEFAULTED 0x0008 +#define SACL_PRESENT 0x0010 +#define SACL_DEFAULTED 0x0020 +#define DACL_TRUSTED 0x0040 +#define SERVER_SECURITY 0x0080 +#define DACL_AUTO_INHERIT_REQ 0x0100 +#define SACL_AUTO_INHERIT_REQ 0x0200 +#define DACL_AUTO_INHERITED 0x0400 +#define SACL_AUTO_INHERITED 0x0800 +#define DACL_PROTECTED 0x1000 +#define SACL_PROTECTED 0x2000 +#define RM_CONTROL_VALID 0x4000 +#define SELF_RELATIVE 0x8000 + +/* ACE types - see MS-DTYP 2.4.4.1 */ +#define ACCESS_ALLOWED_ACE_TYPE 0x00 +#define ACCESS_DENIED_ACE_TYPE 0x01 +#define SYSTEM_AUDIT_ACE_TYPE 0x02 +#define SYSTEM_ALARM_ACE_TYPE 0x03 +#define ACCESS_ALLOWED_COMPOUND_ACE_TYPE 0x04 +#define ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x05 +#define ACCESS_DENIED_OBJECT_ACE_TYPE 0x06 +#define SYSTEM_AUDIT_OBJECT_ACE_TYPE 0x07 +#define SYSTEM_ALARM_OBJECT_ACE_TYPE 0x08 +#define ACCESS_ALLOWED_CALLBACK_ACE_TYPE 0x09 +#define ACCESS_DENIED_CALLBACK_ACE_TYPE 0x0A +#define ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE 0x0B +#define ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE 0x0C +#define SYSTEM_AUDIT_CALLBACK_ACE_TYPE 0x0D +#define SYSTEM_ALARM_CALLBACK_ACE_TYPE 0x0E /* Reserved */ +#define SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE 0x0F +#define SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE 0x10 /* reserved */ +#define SYSTEM_MANDATORY_LABEL_ACE_TYPE 0x11 +#define SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE 0x12 +#define SYSTEM_SCOPED_POLICY_ID_ACE_TYPE 0x13 + +/* ACE flags */ +#define OBJECT_INHERIT_ACE 0x01 +#define CONTAINER_INHERIT_ACE 0x02 +#define NO_PROPAGATE_INHERIT_ACE 0x04 +#define INHERIT_ONLY_ACE 0x08 +#define INHERITED_ACE 0x10 +#define SUCCESSFUL_ACCESS_ACE_FLAG 0x40 +#define FAILED_ACCESS_ACE_FLAG 0x80 + +/* + * Maximum size of a string representation of a SID: + * + * The fields are unsigned values in decimal. So: + * + * u8: max 3 bytes in decimal + * u32: max 10 bytes in decimal + * + * "S-" + 3 bytes for version field + 15 for authority field + NULL terminator + * + * For authority field, max is when all 6 values are non-zero and it must be + * represented in hex. So "-0x" + 12 hex digits. + * + * Add 11 bytes for each subauthority field (10 bytes each + 1 for '-') + */ +#define SID_STRING_BASE_SIZE (2 + 3 + 15 + 1) +#define SID_STRING_SUBAUTH_SIZE (11) /* size of a single subauth string */ + +#define DOMAIN_USER_RID_LE cpu_to_le32(513) + +struct ksmbd_conn; + +struct smb_ntsd { + __le16 revision; /* revision level */ + __le16 type; + __le32 osidoffset; + __le32 gsidoffset; + __le32 sacloffset; + __le32 dacloffset; +} __packed; + +struct smb_sid { + __u8 revision; /* revision level */ + __u8 num_subauth; + __u8 authority[NUM_AUTHS]; + __le32 sub_auth[SID_MAX_SUB_AUTHORITIES]; /* sub_auth[num_subauth] */ +} __packed; + +/* size of a struct cifs_sid, sans sub_auth array */ +#define CIFS_SID_BASE_SIZE (1 + 1 + NUM_AUTHS) + +struct smb_acl { + __le16 revision; /* revision level */ + __le16 size; + __le32 num_aces; +} __packed; + +struct smb_ace { + __u8 type; + __u8 flags; + __le16 size; + __le32 access_req; + struct smb_sid sid; /* ie UUID of user or group who gets these perms */ +} __packed; + +struct smb_fattr { + kuid_t cf_uid; + kgid_t cf_gid; + umode_t cf_mode; + __le32 daccess; + struct posix_acl *cf_acls; + struct posix_acl *cf_dacls; +}; + +struct posix_ace_state { + u32 allow; + u32 deny; +}; + +struct posix_user_ace_state { + union { + kuid_t uid; + kgid_t gid; + }; + struct posix_ace_state perms; +}; + +struct posix_ace_state_array { + int n; + struct posix_user_ace_state aces[]; +}; + +/* + * while processing the nfsv4 ace, this maintains the partial permissions + * calculated so far: + */ + +struct posix_acl_state { + struct posix_ace_state owner; + struct posix_ace_state group; + struct posix_ace_state other; + struct posix_ace_state everyone; + struct posix_ace_state mask; /* deny unused in this case */ + struct posix_ace_state_array *users; + struct posix_ace_state_array *groups; +}; + +int parse_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd, + int acl_len, struct smb_fattr *fattr); +int build_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd, + struct smb_ntsd *ppntsd, int ppntsd_size, int addition_info, + __u32 *secdesclen, struct smb_fattr *fattr); +int init_acl_state(struct posix_acl_state *state, int cnt); +void free_acl_state(struct posix_acl_state *state); +void posix_state_to_acl(struct posix_acl_state *state, + struct posix_acl_entry *pace); +int compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid); +bool smb_inherit_flags(int flags, bool is_dir); +int smb_inherit_dacl(struct ksmbd_conn *conn, const struct path *path, + unsigned int uid, unsigned int gid); +int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, + __le32 *pdaccess, int uid); +int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, + const struct path *path, struct smb_ntsd *pntsd, int ntsd_len, + bool type_check); +void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid); +void ksmbd_init_domain(u32 *sub_auth); + +static inline uid_t posix_acl_uid_translate(struct user_namespace *mnt_userns, + struct posix_acl_entry *pace) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + vfsuid_t vfsuid; +#else + kuid_t kuid; +#endif + + /* If this is an idmapped mount, apply the idmapping. */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 52) && LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0)) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + vfsuid = make_vfsuid(mnt_userns, &init_user_ns, pace->e_uid); +#else + kuid = mapped_kuid_fs(mnt_userns, &init_user_ns, pace->e_uid); +#endif +#else + kuid = kuid_into_mnt(mnt_userns, pace->e_uid); +#endif + + /* Translate the kuid into a userspace id ksmbd would see. */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + return from_kuid(&init_user_ns, vfsuid_into_kuid(vfsuid)); +#else + return from_kuid(&init_user_ns, kuid); +#endif +#else + return from_kuid(&init_user_ns, pace->e_uid); +#endif +} + +static inline gid_t posix_acl_gid_translate(struct user_namespace *mnt_userns, + struct posix_acl_entry *pace) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + vfsgid_t vfsgid; +#else + kgid_t kgid; +#endif + + /* If this is an idmapped mount, apply the idmapping. */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 52) && LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0)) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + vfsgid = make_vfsgid(mnt_userns, &init_user_ns, pace->e_gid); +#else + kgid = mapped_kgid_fs(mnt_userns, &init_user_ns, pace->e_gid); +#endif +#else + kgid = kgid_into_mnt(mnt_userns, pace->e_gid); +#endif + + /* Translate the kgid into a userspace id ksmbd would see. */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + return from_kgid(&init_user_ns, vfsgid_into_kgid(vfsgid)); +#else + return from_kgid(&init_user_ns, kgid); +#endif +#else + return from_kgid(&init_user_ns, pace->e_gid); +#endif +} + +#endif /* _SMBACL_H */ diff --git a/fs/ksmbd/smberr.h b/fs/ksmbd/smberr.h new file mode 100644 index 0000000000000..ce842303ae1f9 --- /dev/null +++ b/fs/ksmbd/smberr.h @@ -0,0 +1,235 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/* + * Copyright (c) International Business Machines Corp., 2002,2004 + * Author(s): Steve French (sfrench@us.ibm.com) + * + * See Error Codes section of the SNIA CIFS Specification + * for more information + */ +#ifndef __KSMBD_SMBERR_H +#define __KSMBD_SMBERR_H + +#define SUCCESS 0x00 /* The request was successful. */ +#define ERRDOS 0x01 /* Error is from the core DOS operating system set */ +#define ERRSRV 0x02 /* Error is generated by the file server daemon */ +#define ERRHRD 0x03 /* Error is a hardware error. */ +#define ERRCMD 0xFF /* Command was not in the "SMB" format. */ + +/* The following error codes may be generated with the SUCCESS error class.*/ + +/*#define SUCCESS 0 The request was successful. */ + +/* The following error codes may be generated with the ERRDOS error class.*/ + +#define ERRbadfunc 1 /* + * Invalid function. The server did not + * recognize or could not perform a + * system call generated by the server, + * e.g., set the DIRECTORY attribute on + * a data file, invalid seek mode. + */ +#define ERRbadfile 2 /* + * File not found. The last component + * of a file's pathname could not be + * found. + */ +#define ERRbadpath 3 /* + * Directory invalid. A directory + * component in a pathname could not be + * found. + */ +#define ERRnofids 4 /* + * Too many open files. The server has + * no file handles available. + */ +#define ERRnoaccess 5 /* + * Access denied, the client's context + * does not permit the requested + * function. This includes the + * following conditions: invalid rename + * command, write to Fid open for read + * only, read on Fid open for write + * only, attempt to delete a non-empty + * directory + */ +#define ERRbadfid 6 /* + * Invalid file handle. The file handle + * specified was not recognized by the + * server. + */ +#define ERRbadmcb 7 /* Memory control blocks destroyed. */ +#define ERRnomem 8 /* + * Insufficient server memory to + * perform the requested function. + */ +#define ERRbadmem 9 /* Invalid memory block address. */ +#define ERRbadenv 10 /* Invalid environment. */ +#define ERRbadformat 11 /* Invalid format. */ +#define ERRbadaccess 12 /* Invalid open mode. */ +#define ERRbaddata 13 /* + * Invalid data (generated only by + * IOCTL calls within the server). + */ +#define ERRbaddrive 15 /* Invalid drive specified. */ +#define ERRremcd 16 /* + * A Delete Directory request attempted + * to remove the server's current + * directory. + */ +#define ERRdiffdevice 17 /* + * Not same device (e.g., a cross + * volume rename was attempted + */ +#define ERRnofiles 18 /* + * A File Search command can find no + * more files matching the specified + * criteria. + */ +#define ERRwriteprot 19 /* media is write protected */ +#define ERRgeneral 31 +#define ERRbadshare 32 /* + * The sharing mode specified for an + * Open conflicts with existing FIDs on + * the file. + */ +#define ERRlock 33 /* + * A Lock request conflicted with an + * existing lock or specified an + * invalid mode, or an Unlock requested + * attempted to remove a lock held by + * another process. + */ +#define ERRunsup 50 +#define ERRnosuchshare 67 +#define ERRfilexists 80 /* + * The file named in the request + * already exists. + */ +#define ERRinvparm 87 +#define ERRdiskfull 112 +#define ERRinvname 123 +#define ERRinvlevel 124 +#define ERRdirnotempty 145 +#define ERRnotlocked 158 +#define ERRcancelviolation 173 +#define ERRnoatomiclocks 174 +#define ERRalreadyexists 183 +#define ERRbadpipe 230 +#define ERRpipebusy 231 +#define ERRpipeclosing 232 +#define ERRnotconnected 233 +#define ERRmoredata 234 +#define ERReasnotsupported 282 +#define ErrQuota 0x200 /* + * The operation would cause a quota + * limit to be exceeded. + */ +#define ErrNotALink 0x201 /* + * A link operation was performed on a + * pathname that was not a link. + */ + +/* + * Below errors are used internally (do not come over the wire) for passthrough + * from STATUS codes to POSIX only + */ +#define ERRsymlink 0xFFFD +#define ErrTooManyLinks 0xFFFE + +/* Following error codes may be generated with the ERRSRV error class.*/ + +#define ERRerror 1 /* + * Non-specific error code. It is + * returned under the following + * conditions: resource other than disk + * space exhausted (e.g. TIDs), first + * SMB command was not negotiate, + * multiple negotiates attempted, and + * internal server error. + */ +#define ERRbadpw 2 /* + * Bad password - name/password pair in + * a TreeConnect or Session Setup are + * invalid. + */ +#define ERRbadtype 3 /* + * used for indicating DFS referral + * needed + */ +#define ERRaccess 4 /* + * The client does not have the + * necessary access rights within the + * specified context for requested + * function. + */ +#define ERRinvtid 5 /* + * The Tid specified in a command was + * invalid. + */ +#define ERRinvnetname 6 /* + * Invalid network name in tree + * connect. + */ +#define ERRinvdevice 7 /* + * Invalid device - printer request + * made to non-printer connection or + * non-printer request made to printer + * connection. + */ +#define ERRqfull 49 /* + * Print queue full (files) -- returned + * by open print file. + */ +#define ERRqtoobig 50 /* Print queue full -- no space. */ +#define ERRqeof 51 /* EOF on print queue dump */ +#define ERRinvpfid 52 /* Invalid print file FID. */ +#define ERRsmbcmd 64 /* + * The server did not recognize the + * command received. + */ +#define ERRsrverror 65 /* + * The server encountered an internal + * error, e.g., system file + * unavailable. + */ +#define ERRbadBID 66 /* (obsolete) */ +#define ERRfilespecs 67 /* + * The Fid and pathname parameters + * contained an invalid combination of + * values. + */ +#define ERRbadLink 68 /* (obsolete) */ +#define ERRbadpermits 69 /* + * The access permissions specified for + * a file or directory are not a valid + * combination. + */ +#define ERRbadPID 70 +#define ERRsetattrmode 71 /* attribute (mode) is invalid */ +#define ERRpaused 81 /* Server is paused */ +#define ERRmsgoff 82 /* reserved - messaging off */ +#define ERRnoroom 83 /* reserved - no room for message */ +#define ERRrmuns 87 /* reserved - too many remote names */ +#define ERRtimeout 88 /* operation timed out */ +#define ERRnoresource 89 /* No resources available for request */ +#define ERRtoomanyuids 90 /* + * Too many UIDs active on this session + */ +#define ERRbaduid 91 /* + * The UID is not known as a valid user + */ +#define ERRusempx 250 /* temporarily unable to use raw */ +#define ERRusestd 251 /* + * temporarily unable to use either raw + * or mpx + */ +#define ERR_NOTIFY_ENUM_DIR 1024 +#define ERRnoSuchUser 2238 /* user account does not exist */ +#define ERRaccountexpired 2239 +#define ERRbadclient 2240 /* can not logon from this client */ +#define ERRbadLogonTime 2241 /* logon hours do not allow this */ +#define ERRpasswordExpired 2242 +#define ERRnetlogonNotStarted 2455 +#define ERRnosupport 0xFFFF + +#endif /* __KSMBD_SMBERR_H */ diff --git a/fs/ksmbd/smbfsctl.h b/fs/ksmbd/smbfsctl.h new file mode 100644 index 0000000000000..b98418aae20cd --- /dev/null +++ b/fs/ksmbd/smbfsctl.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/* + * fs/cifs/smbfsctl.h: SMB, CIFS, SMB2 FSCTL definitions + * + * Copyright (c) International Business Machines Corp., 2002,2009 + * Author(s): Steve French (sfrench@us.ibm.com) + */ + +/* IOCTL information */ +/* + * List of ioctl/fsctl function codes that are or could be useful in the + * future to remote clients like cifs or SMB2 client. There is probably + * a slightly larger set of fsctls that NTFS local filesystem could handle, + * including the seven below that we do not have struct definitions for. + * Even with protocol definitions for most of these now available, we still + * need to do some experimentation to identify which are practical to do + * remotely. Some of the following, such as the encryption/compression ones + * could be invoked from tools via a specialized hook into the VFS rather + * than via the standard vfs entry points + */ + +#ifndef __KSMBD_SMBFSCTL_H +#define __KSMBD_SMBFSCTL_H + +#define FSCTL_DFS_GET_REFERRALS 0x00060194 +#define FSCTL_DFS_GET_REFERRALS_EX 0x000601B0 +#define FSCTL_REQUEST_OPLOCK_LEVEL_1 0x00090000 +#define FSCTL_REQUEST_OPLOCK_LEVEL_2 0x00090004 +#define FSCTL_REQUEST_BATCH_OPLOCK 0x00090008 +#define FSCTL_LOCK_VOLUME 0x00090018 +#define FSCTL_UNLOCK_VOLUME 0x0009001C +#define FSCTL_IS_PATHNAME_VALID 0x0009002C /* BB add struct */ +#define FSCTL_GET_COMPRESSION 0x0009003C /* BB add struct */ +#define FSCTL_SET_COMPRESSION 0x0009C040 /* BB add struct */ +#define FSCTL_QUERY_FAT_BPB 0x00090058 /* BB add struct */ +/* Verify the next FSCTL number, we had it as 0x00090090 before */ +#define FSCTL_FILESYSTEM_GET_STATS 0x00090060 /* BB add struct */ +#define FSCTL_GET_NTFS_VOLUME_DATA 0x00090064 /* BB add struct */ +#define FSCTL_GET_RETRIEVAL_POINTERS 0x00090073 /* BB add struct */ +#define FSCTL_IS_VOLUME_DIRTY 0x00090078 /* BB add struct */ +#define FSCTL_ALLOW_EXTENDED_DASD_IO 0x00090083 /* BB add struct */ +#define FSCTL_REQUEST_FILTER_OPLOCK 0x0009008C +#define FSCTL_FIND_FILES_BY_SID 0x0009008F /* BB add struct */ +#define FSCTL_SET_OBJECT_ID 0x00090098 /* BB add struct */ +#define FSCTL_GET_OBJECT_ID 0x0009009C /* BB add struct */ +#define FSCTL_DELETE_OBJECT_ID 0x000900A0 /* BB add struct */ +#define FSCTL_SET_REPARSE_POINT 0x000900A4 /* BB add struct */ +#define FSCTL_GET_REPARSE_POINT 0x000900A8 /* BB add struct */ +#define FSCTL_DELETE_REPARSE_POINT 0x000900AC /* BB add struct */ +#define FSCTL_SET_OBJECT_ID_EXTENDED 0x000900BC /* BB add struct */ +#define FSCTL_CREATE_OR_GET_OBJECT_ID 0x000900C0 /* BB add struct */ +#define FSCTL_SET_SPARSE 0x000900C4 /* BB add struct */ +#define FSCTL_SET_ZERO_DATA 0x000980C8 /* BB add struct */ +#define FSCTL_SET_ENCRYPTION 0x000900D7 /* BB add struct */ +#define FSCTL_ENCRYPTION_FSCTL_IO 0x000900DB /* BB add struct */ +#define FSCTL_WRITE_RAW_ENCRYPTED 0x000900DF /* BB add struct */ +#define FSCTL_READ_RAW_ENCRYPTED 0x000900E3 /* BB add struct */ +#define FSCTL_READ_FILE_USN_DATA 0x000900EB /* BB add struct */ +#define FSCTL_WRITE_USN_CLOSE_RECORD 0x000900EF /* BB add struct */ +#define FSCTL_SIS_COPYFILE 0x00090100 /* BB add struct */ +#define FSCTL_RECALL_FILE 0x00090117 /* BB add struct */ +#define FSCTL_QUERY_SPARING_INFO 0x00090138 /* BB add struct */ +#define FSCTL_SET_ZERO_ON_DEALLOC 0x00090194 /* BB add struct */ +#define FSCTL_SET_SHORT_NAME_BEHAVIOR 0x000901B4 /* BB add struct */ +#define FSCTL_QUERY_ALLOCATED_RANGES 0x000940CF /* BB add struct */ +#define FSCTL_SET_DEFECT_MANAGEMENT 0x00098134 /* BB add struct */ +#define FSCTL_DUPLICATE_EXTENTS_TO_FILE 0x00098344 +#define FSCTL_SIS_LINK_FILES 0x0009C104 +#define FSCTL_PIPE_PEEK 0x0011400C /* BB add struct */ +#define FSCTL_PIPE_TRANSCEIVE 0x0011C017 /* BB add struct */ +/* strange that the number for this op is not sequential with previous op */ +#define FSCTL_PIPE_WAIT 0x00110018 /* BB add struct */ +#define FSCTL_REQUEST_RESUME_KEY 0x00140078 +#define FSCTL_LMR_GET_LINK_TRACK_INF 0x001400E8 /* BB add struct */ +#define FSCTL_LMR_SET_LINK_TRACK_INF 0x001400EC /* BB add struct */ +#define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204 +#define FSCTL_QUERY_NETWORK_INTERFACE_INFO 0x001401FC +#define FSCTL_COPYCHUNK 0x001440F2 +#define FSCTL_COPYCHUNK_WRITE 0x001480F2 + +#define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003 +#define IO_REPARSE_TAG_HSM 0xC0000004 +#define IO_REPARSE_TAG_SIS 0x80000007 + +/* WSL reparse tags */ +#define IO_REPARSE_TAG_LX_SYMLINK_LE cpu_to_le32(0xA000001D) +#define IO_REPARSE_TAG_AF_UNIX_LE cpu_to_le32(0x80000023) +#define IO_REPARSE_TAG_LX_FIFO_LE cpu_to_le32(0x80000024) +#define IO_REPARSE_TAG_LX_CHR_LE cpu_to_le32(0x80000025) +#define IO_REPARSE_TAG_LX_BLK_LE cpu_to_le32(0x80000026) +#endif /* __KSMBD_SMBFSCTL_H */ diff --git a/fs/ksmbd/smbstatus.h b/fs/ksmbd/smbstatus.h new file mode 100644 index 0000000000000..108a8b6ed24a0 --- /dev/null +++ b/fs/ksmbd/smbstatus.h @@ -0,0 +1,1822 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/* + * fs/cifs/smb2status.h + * + * SMB2 Status code (network error) definitions + * Definitions are from MS-ERREF + * + * Copyright (c) International Business Machines Corp., 2009,2011 + * Author(s): Steve French (sfrench@us.ibm.com) + */ + +/* + * 0 1 2 3 4 5 6 7 8 9 0 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F + * SEV C N <-------Facility--------> <------Error Status Code------> + * + * C is set if "customer defined" error, N bit is reserved and MBZ + */ + +#define STATUS_SEVERITY_SUCCESS cpu_to_le32(0x0000) +#define STATUS_SEVERITY_INFORMATIONAL cpu_to_le32(0x0001) +#define STATUS_SEVERITY_WARNING cpu_to_le32(0x0002) +#define STATUS_SEVERITY_ERROR cpu_to_le32(0x0003) + +struct ntstatus { + /* Facility is the high 12 bits of the following field */ + __le32 Facility; /* low 2 bits Severity, next is Customer, then rsrvd */ + __le32 Code; +}; + +#define STATUS_SUCCESS 0x00000000 +#define STATUS_WAIT_0 cpu_to_le32(0x00000000) +#define STATUS_WAIT_1 cpu_to_le32(0x00000001) +#define STATUS_WAIT_2 cpu_to_le32(0x00000002) +#define STATUS_WAIT_3 cpu_to_le32(0x00000003) +#define STATUS_WAIT_63 cpu_to_le32(0x0000003F) +#define STATUS_ABANDONED cpu_to_le32(0x00000080) +#define STATUS_ABANDONED_WAIT_0 cpu_to_le32(0x00000080) +#define STATUS_ABANDONED_WAIT_63 cpu_to_le32(0x000000BF) +#define STATUS_USER_APC cpu_to_le32(0x000000C0) +#define STATUS_KERNEL_APC cpu_to_le32(0x00000100) +#define STATUS_ALERTED cpu_to_le32(0x00000101) +#define STATUS_TIMEOUT cpu_to_le32(0x00000102) +#define STATUS_PENDING cpu_to_le32(0x00000103) +#define STATUS_REPARSE cpu_to_le32(0x00000104) +#define STATUS_MORE_ENTRIES cpu_to_le32(0x00000105) +#define STATUS_NOT_ALL_ASSIGNED cpu_to_le32(0x00000106) +#define STATUS_SOME_NOT_MAPPED cpu_to_le32(0x00000107) +#define STATUS_OPLOCK_BREAK_IN_PROGRESS cpu_to_le32(0x00000108) +#define STATUS_VOLUME_MOUNTED cpu_to_le32(0x00000109) +#define STATUS_RXACT_COMMITTED cpu_to_le32(0x0000010A) +#define STATUS_NOTIFY_CLEANUP cpu_to_le32(0x0000010B) +#define STATUS_NOTIFY_ENUM_DIR cpu_to_le32(0x0000010C) +#define STATUS_NO_QUOTAS_FOR_ACCOUNT cpu_to_le32(0x0000010D) +#define STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED cpu_to_le32(0x0000010E) +#define STATUS_PAGE_FAULT_TRANSITION cpu_to_le32(0x00000110) +#define STATUS_PAGE_FAULT_DEMAND_ZERO cpu_to_le32(0x00000111) +#define STATUS_PAGE_FAULT_COPY_ON_WRITE cpu_to_le32(0x00000112) +#define STATUS_PAGE_FAULT_GUARD_PAGE cpu_to_le32(0x00000113) +#define STATUS_PAGE_FAULT_PAGING_FILE cpu_to_le32(0x00000114) +#define STATUS_CACHE_PAGE_LOCKED cpu_to_le32(0x00000115) +#define STATUS_CRASH_DUMP cpu_to_le32(0x00000116) +#define STATUS_BUFFER_ALL_ZEROS cpu_to_le32(0x00000117) +#define STATUS_REPARSE_OBJECT cpu_to_le32(0x00000118) +#define STATUS_RESOURCE_REQUIREMENTS_CHANGED cpu_to_le32(0x00000119) +#define STATUS_TRANSLATION_COMPLETE cpu_to_le32(0x00000120) +#define STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY cpu_to_le32(0x00000121) +#define STATUS_NOTHING_TO_TERMINATE cpu_to_le32(0x00000122) +#define STATUS_PROCESS_NOT_IN_JOB cpu_to_le32(0x00000123) +#define STATUS_PROCESS_IN_JOB cpu_to_le32(0x00000124) +#define STATUS_VOLSNAP_HIBERNATE_READY cpu_to_le32(0x00000125) +#define STATUS_FSFILTER_OP_COMPLETED_SUCCESSFULLY cpu_to_le32(0x00000126) +#define STATUS_INTERRUPT_VECTOR_ALREADY_CONNECTED cpu_to_le32(0x00000127) +#define STATUS_INTERRUPT_STILL_CONNECTED cpu_to_le32(0x00000128) +#define STATUS_PROCESS_CLONED cpu_to_le32(0x00000129) +#define STATUS_FILE_LOCKED_WITH_ONLY_READERS cpu_to_le32(0x0000012A) +#define STATUS_FILE_LOCKED_WITH_WRITERS cpu_to_le32(0x0000012B) +#define STATUS_RESOURCEMANAGER_READ_ONLY cpu_to_le32(0x00000202) +#define STATUS_WAIT_FOR_OPLOCK cpu_to_le32(0x00000367) +#define DBG_EXCEPTION_HANDLED cpu_to_le32(0x00010001) +#define DBG_CONTINUE cpu_to_le32(0x00010002) +#define STATUS_FLT_IO_COMPLETE cpu_to_le32(0x001C0001) +#define STATUS_OBJECT_NAME_EXISTS cpu_to_le32(0x40000000) +#define STATUS_THREAD_WAS_SUSPENDED cpu_to_le32(0x40000001) +#define STATUS_WORKING_SET_LIMIT_RANGE cpu_to_le32(0x40000002) +#define STATUS_IMAGE_NOT_AT_BASE cpu_to_le32(0x40000003) +#define STATUS_RXACT_STATE_CREATED cpu_to_le32(0x40000004) +#define STATUS_SEGMENT_NOTIFICATION cpu_to_le32(0x40000005) +#define STATUS_LOCAL_USER_SESSION_KEY cpu_to_le32(0x40000006) +#define STATUS_BAD_CURRENT_DIRECTORY cpu_to_le32(0x40000007) +#define STATUS_SERIAL_MORE_WRITES cpu_to_le32(0x40000008) +#define STATUS_REGISTRY_RECOVERED cpu_to_le32(0x40000009) +#define STATUS_FT_READ_RECOVERY_FROM_BACKUP cpu_to_le32(0x4000000A) +#define STATUS_FT_WRITE_RECOVERY cpu_to_le32(0x4000000B) +#define STATUS_SERIAL_COUNTER_TIMEOUT cpu_to_le32(0x4000000C) +#define STATUS_NULL_LM_PASSWORD cpu_to_le32(0x4000000D) +#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH cpu_to_le32(0x4000000E) +#define STATUS_RECEIVE_PARTIAL cpu_to_le32(0x4000000F) +#define STATUS_RECEIVE_EXPEDITED cpu_to_le32(0x40000010) +#define STATUS_RECEIVE_PARTIAL_EXPEDITED cpu_to_le32(0x40000011) +#define STATUS_EVENT_DONE cpu_to_le32(0x40000012) +#define STATUS_EVENT_PENDING cpu_to_le32(0x40000013) +#define STATUS_CHECKING_FILE_SYSTEM cpu_to_le32(0x40000014) +#define STATUS_FATAL_APP_EXIT cpu_to_le32(0x40000015) +#define STATUS_PREDEFINED_HANDLE cpu_to_le32(0x40000016) +#define STATUS_WAS_UNLOCKED cpu_to_le32(0x40000017) +#define STATUS_SERVICE_NOTIFICATION cpu_to_le32(0x40000018) +#define STATUS_WAS_LOCKED cpu_to_le32(0x40000019) +#define STATUS_LOG_HARD_ERROR cpu_to_le32(0x4000001A) +#define STATUS_ALREADY_WIN32 cpu_to_le32(0x4000001B) +#define STATUS_WX86_UNSIMULATE cpu_to_le32(0x4000001C) +#define STATUS_WX86_CONTINUE cpu_to_le32(0x4000001D) +#define STATUS_WX86_SINGLE_STEP cpu_to_le32(0x4000001E) +#define STATUS_WX86_BREAKPOINT cpu_to_le32(0x4000001F) +#define STATUS_WX86_EXCEPTION_CONTINUE cpu_to_le32(0x40000020) +#define STATUS_WX86_EXCEPTION_LASTCHANCE cpu_to_le32(0x40000021) +#define STATUS_WX86_EXCEPTION_CHAIN cpu_to_le32(0x40000022) +#define STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE cpu_to_le32(0x40000023) +#define STATUS_NO_YIELD_PERFORMED cpu_to_le32(0x40000024) +#define STATUS_TIMER_RESUME_IGNORED cpu_to_le32(0x40000025) +#define STATUS_ARBITRATION_UNHANDLED cpu_to_le32(0x40000026) +#define STATUS_CARDBUS_NOT_SUPPORTED cpu_to_le32(0x40000027) +#define STATUS_WX86_CREATEWX86TIB cpu_to_le32(0x40000028) +#define STATUS_MP_PROCESSOR_MISMATCH cpu_to_le32(0x40000029) +#define STATUS_HIBERNATED cpu_to_le32(0x4000002A) +#define STATUS_RESUME_HIBERNATION cpu_to_le32(0x4000002B) +#define STATUS_FIRMWARE_UPDATED cpu_to_le32(0x4000002C) +#define STATUS_DRIVERS_LEAKING_LOCKED_PAGES cpu_to_le32(0x4000002D) +#define STATUS_MESSAGE_RETRIEVED cpu_to_le32(0x4000002E) +#define STATUS_SYSTEM_POWERSTATE_TRANSITION cpu_to_le32(0x4000002F) +#define STATUS_ALPC_CHECK_COMPLETION_LIST cpu_to_le32(0x40000030) +#define STATUS_SYSTEM_POWERSTATE_COMPLEX_TRANSITION cpu_to_le32(0x40000031) +#define STATUS_ACCESS_AUDIT_BY_POLICY cpu_to_le32(0x40000032) +#define STATUS_ABANDON_HIBERFILE cpu_to_le32(0x40000033) +#define STATUS_BIZRULES_NOT_ENABLED cpu_to_le32(0x40000034) +#define STATUS_WAKE_SYSTEM cpu_to_le32(0x40000294) +#define STATUS_DS_SHUTTING_DOWN cpu_to_le32(0x40000370) +#define DBG_REPLY_LATER cpu_to_le32(0x40010001) +#define DBG_UNABLE_TO_PROVIDE_HANDLE cpu_to_le32(0x40010002) +#define DBG_TERMINATE_THREAD cpu_to_le32(0x40010003) +#define DBG_TERMINATE_PROCESS cpu_to_le32(0x40010004) +#define DBG_CONTROL_C cpu_to_le32(0x40010005) +#define DBG_PRINTEXCEPTION_C cpu_to_le32(0x40010006) +#define DBG_RIPEXCEPTION cpu_to_le32(0x40010007) +#define DBG_CONTROL_BREAK cpu_to_le32(0x40010008) +#define DBG_COMMAND_EXCEPTION cpu_to_le32(0x40010009) +#define RPC_NT_UUID_LOCAL_ONLY cpu_to_le32(0x40020056) +#define RPC_NT_SEND_INCOMPLETE cpu_to_le32(0x400200AF) +#define STATUS_CTX_CDM_CONNECT cpu_to_le32(0x400A0004) +#define STATUS_CTX_CDM_DISCONNECT cpu_to_le32(0x400A0005) +#define STATUS_SXS_RELEASE_ACTIVATION_CONTEXT cpu_to_le32(0x4015000D) +#define STATUS_RECOVERY_NOT_NEEDED cpu_to_le32(0x40190034) +#define STATUS_RM_ALREADY_STARTED cpu_to_le32(0x40190035) +#define STATUS_LOG_NO_RESTART cpu_to_le32(0x401A000C) +#define STATUS_VIDEO_DRIVER_DEBUG_REPORT_REQUEST cpu_to_le32(0x401B00EC) +#define STATUS_GRAPHICS_PARTIAL_DATA_POPULATED cpu_to_le32(0x401E000A) +#define STATUS_GRAPHICS_DRIVER_MISMATCH cpu_to_le32(0x401E0117) +#define STATUS_GRAPHICS_MODE_NOT_PINNED cpu_to_le32(0x401E0307) +#define STATUS_GRAPHICS_NO_PREFERRED_MODE cpu_to_le32(0x401E031E) +#define STATUS_GRAPHICS_DATASET_IS_EMPTY cpu_to_le32(0x401E034B) +#define STATUS_GRAPHICS_NO_MORE_ELEMENTS_IN_DATASET cpu_to_le32(0x401E034C) +#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_PINNED \ + cpu_to_le32(0x401E0351) +#define STATUS_GRAPHICS_UNKNOWN_CHILD_STATUS cpu_to_le32(0x401E042F) +#define STATUS_GRAPHICS_LEADLINK_START_DEFERRED cpu_to_le32(0x401E0437) +#define STATUS_GRAPHICS_POLLING_TOO_FREQUENTLY cpu_to_le32(0x401E0439) +#define STATUS_GRAPHICS_START_DEFERRED cpu_to_le32(0x401E043A) +#define STATUS_NDIS_INDICATION_REQUIRED cpu_to_le32(0x40230001) +#define STATUS_GUARD_PAGE_VIOLATION cpu_to_le32(0x80000001) +#define STATUS_DATATYPE_MISALIGNMENT cpu_to_le32(0x80000002) +#define STATUS_BREAKPOINT cpu_to_le32(0x80000003) +#define STATUS_SINGLE_STEP cpu_to_le32(0x80000004) +#define STATUS_BUFFER_OVERFLOW cpu_to_le32(0x80000005) +#define STATUS_NO_MORE_FILES cpu_to_le32(0x80000006) +#define STATUS_WAKE_SYSTEM_DEBUGGER cpu_to_le32(0x80000007) +#define STATUS_HANDLES_CLOSED cpu_to_le32(0x8000000A) +#define STATUS_NO_INHERITANCE cpu_to_le32(0x8000000B) +#define STATUS_GUID_SUBSTITUTION_MADE cpu_to_le32(0x8000000C) +#define STATUS_PARTIAL_COPY cpu_to_le32(0x8000000D) +#define STATUS_DEVICE_PAPER_EMPTY cpu_to_le32(0x8000000E) +#define STATUS_DEVICE_POWERED_OFF cpu_to_le32(0x8000000F) +#define STATUS_DEVICE_OFF_LINE cpu_to_le32(0x80000010) +#define STATUS_DEVICE_BUSY cpu_to_le32(0x80000011) +#define STATUS_NO_MORE_EAS cpu_to_le32(0x80000012) +#define STATUS_INVALID_EA_NAME cpu_to_le32(0x80000013) +#define STATUS_EA_LIST_INCONSISTENT cpu_to_le32(0x80000014) +#define STATUS_INVALID_EA_FLAG cpu_to_le32(0x80000015) +#define STATUS_VERIFY_REQUIRED cpu_to_le32(0x80000016) +#define STATUS_EXTRANEOUS_INFORMATION cpu_to_le32(0x80000017) +#define STATUS_RXACT_COMMIT_NECESSARY cpu_to_le32(0x80000018) +#define STATUS_NO_MORE_ENTRIES cpu_to_le32(0x8000001A) +#define STATUS_FILEMARK_DETECTED cpu_to_le32(0x8000001B) +#define STATUS_MEDIA_CHANGED cpu_to_le32(0x8000001C) +#define STATUS_BUS_RESET cpu_to_le32(0x8000001D) +#define STATUS_END_OF_MEDIA cpu_to_le32(0x8000001E) +#define STATUS_BEGINNING_OF_MEDIA cpu_to_le32(0x8000001F) +#define STATUS_MEDIA_CHECK cpu_to_le32(0x80000020) +#define STATUS_SETMARK_DETECTED cpu_to_le32(0x80000021) +#define STATUS_NO_DATA_DETECTED cpu_to_le32(0x80000022) +#define STATUS_REDIRECTOR_HAS_OPEN_HANDLES cpu_to_le32(0x80000023) +#define STATUS_SERVER_HAS_OPEN_HANDLES cpu_to_le32(0x80000024) +#define STATUS_ALREADY_DISCONNECTED cpu_to_le32(0x80000025) +#define STATUS_LONGJUMP cpu_to_le32(0x80000026) +#define STATUS_CLEANER_CARTRIDGE_INSTALLED cpu_to_le32(0x80000027) +#define STATUS_PLUGPLAY_QUERY_VETOED cpu_to_le32(0x80000028) +#define STATUS_UNWIND_CONSOLIDATE cpu_to_le32(0x80000029) +#define STATUS_REGISTRY_HIVE_RECOVERED cpu_to_le32(0x8000002A) +#define STATUS_DLL_MIGHT_BE_INSECURE cpu_to_le32(0x8000002B) +#define STATUS_DLL_MIGHT_BE_INCOMPATIBLE cpu_to_le32(0x8000002C) +#define STATUS_STOPPED_ON_SYMLINK cpu_to_le32(0x8000002D) +#define STATUS_DEVICE_REQUIRES_CLEANING cpu_to_le32(0x80000288) +#define STATUS_DEVICE_DOOR_OPEN cpu_to_le32(0x80000289) +#define STATUS_DATA_LOST_REPAIR cpu_to_le32(0x80000803) +#define DBG_EXCEPTION_NOT_HANDLED cpu_to_le32(0x80010001) +#define STATUS_CLUSTER_NODE_ALREADY_UP cpu_to_le32(0x80130001) +#define STATUS_CLUSTER_NODE_ALREADY_DOWN cpu_to_le32(0x80130002) +#define STATUS_CLUSTER_NETWORK_ALREADY_ONLINE cpu_to_le32(0x80130003) +#define STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE cpu_to_le32(0x80130004) +#define STATUS_CLUSTER_NODE_ALREADY_MEMBER cpu_to_le32(0x80130005) +#define STATUS_COULD_NOT_RESIZE_LOG cpu_to_le32(0x80190009) +#define STATUS_NO_TXF_METADATA cpu_to_le32(0x80190029) +#define STATUS_CANT_RECOVER_WITH_HANDLE_OPEN cpu_to_le32(0x80190031) +#define STATUS_TXF_METADATA_ALREADY_PRESENT cpu_to_le32(0x80190041) +#define STATUS_TRANSACTION_SCOPE_CALLBACKS_NOT_SET cpu_to_le32(0x80190042) +#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD_RECOVERED \ + cpu_to_le32(0x801B00EB) +#define STATUS_FLT_BUFFER_TOO_SMALL cpu_to_le32(0x801C0001) +#define STATUS_FVE_PARTIAL_METADATA cpu_to_le32(0x80210001) +#define STATUS_UNSUCCESSFUL cpu_to_le32(0xC0000001) +#define STATUS_NOT_IMPLEMENTED cpu_to_le32(0xC0000002) +#define STATUS_INVALID_INFO_CLASS cpu_to_le32(0xC0000003) +#define STATUS_INFO_LENGTH_MISMATCH cpu_to_le32(0xC0000004) +#define STATUS_ACCESS_VIOLATION cpu_to_le32(0xC0000005) +#define STATUS_IN_PAGE_ERROR cpu_to_le32(0xC0000006) +#define STATUS_PAGEFILE_QUOTA cpu_to_le32(0xC0000007) +#define STATUS_INVALID_HANDLE cpu_to_le32(0xC0000008) +#define STATUS_BAD_INITIAL_STACK cpu_to_le32(0xC0000009) +#define STATUS_BAD_INITIAL_PC cpu_to_le32(0xC000000A) +#define STATUS_INVALID_CID cpu_to_le32(0xC000000B) +#define STATUS_TIMER_NOT_CANCELED cpu_to_le32(0xC000000C) +#define STATUS_INVALID_PARAMETER cpu_to_le32(0xC000000D) +#define STATUS_NO_SUCH_DEVICE cpu_to_le32(0xC000000E) +#define STATUS_NO_SUCH_FILE cpu_to_le32(0xC000000F) +#define STATUS_INVALID_DEVICE_REQUEST cpu_to_le32(0xC0000010) +#define STATUS_END_OF_FILE cpu_to_le32(0xC0000011) +#define STATUS_WRONG_VOLUME cpu_to_le32(0xC0000012) +#define STATUS_NO_MEDIA_IN_DEVICE cpu_to_le32(0xC0000013) +#define STATUS_UNRECOGNIZED_MEDIA cpu_to_le32(0xC0000014) +#define STATUS_NONEXISTENT_SECTOR cpu_to_le32(0xC0000015) +#define STATUS_MORE_PROCESSING_REQUIRED cpu_to_le32(0xC0000016) +#define STATUS_NO_MEMORY cpu_to_le32(0xC0000017) +#define STATUS_CONFLICTING_ADDRESSES cpu_to_le32(0xC0000018) +#define STATUS_NOT_MAPPED_VIEW cpu_to_le32(0xC0000019) +#define STATUS_UNABLE_TO_FREE_VM cpu_to_le32(0xC000001A) +#define STATUS_UNABLE_TO_DELETE_SECTION cpu_to_le32(0xC000001B) +#define STATUS_INVALID_SYSTEM_SERVICE cpu_to_le32(0xC000001C) +#define STATUS_ILLEGAL_INSTRUCTION cpu_to_le32(0xC000001D) +#define STATUS_INVALID_LOCK_SEQUENCE cpu_to_le32(0xC000001E) +#define STATUS_INVALID_VIEW_SIZE cpu_to_le32(0xC000001F) +#define STATUS_INVALID_FILE_FOR_SECTION cpu_to_le32(0xC0000020) +#define STATUS_ALREADY_COMMITTED cpu_to_le32(0xC0000021) +#define STATUS_ACCESS_DENIED cpu_to_le32(0xC0000022) +#define STATUS_BUFFER_TOO_SMALL cpu_to_le32(0xC0000023) +#define STATUS_OBJECT_TYPE_MISMATCH cpu_to_le32(0xC0000024) +#define STATUS_NONCONTINUABLE_EXCEPTION cpu_to_le32(0xC0000025) +#define STATUS_INVALID_DISPOSITION cpu_to_le32(0xC0000026) +#define STATUS_UNWIND cpu_to_le32(0xC0000027) +#define STATUS_BAD_STACK cpu_to_le32(0xC0000028) +#define STATUS_INVALID_UNWIND_TARGET cpu_to_le32(0xC0000029) +#define STATUS_NOT_LOCKED cpu_to_le32(0xC000002A) +#define STATUS_PARITY_ERROR cpu_to_le32(0xC000002B) +#define STATUS_UNABLE_TO_DECOMMIT_VM cpu_to_le32(0xC000002C) +#define STATUS_NOT_COMMITTED cpu_to_le32(0xC000002D) +#define STATUS_INVALID_PORT_ATTRIBUTES cpu_to_le32(0xC000002E) +#define STATUS_PORT_MESSAGE_TOO_LONG cpu_to_le32(0xC000002F) +#define STATUS_INVALID_PARAMETER_MIX cpu_to_le32(0xC0000030) +#define STATUS_INVALID_QUOTA_LOWER cpu_to_le32(0xC0000031) +#define STATUS_DISK_CORRUPT_ERROR cpu_to_le32(0xC0000032) +#define STATUS_OBJECT_NAME_INVALID cpu_to_le32(0xC0000033) +#define STATUS_OBJECT_NAME_NOT_FOUND cpu_to_le32(0xC0000034) +#define STATUS_OBJECT_NAME_COLLISION cpu_to_le32(0xC0000035) +#define STATUS_PORT_DISCONNECTED cpu_to_le32(0xC0000037) +#define STATUS_DEVICE_ALREADY_ATTACHED cpu_to_le32(0xC0000038) +#define STATUS_OBJECT_PATH_INVALID cpu_to_le32(0xC0000039) +#define STATUS_OBJECT_PATH_NOT_FOUND cpu_to_le32(0xC000003A) +#define STATUS_OBJECT_PATH_SYNTAX_BAD cpu_to_le32(0xC000003B) +#define STATUS_DATA_OVERRUN cpu_to_le32(0xC000003C) +#define STATUS_DATA_LATE_ERROR cpu_to_le32(0xC000003D) +#define STATUS_DATA_ERROR cpu_to_le32(0xC000003E) +#define STATUS_CRC_ERROR cpu_to_le32(0xC000003F) +#define STATUS_SECTION_TOO_BIG cpu_to_le32(0xC0000040) +#define STATUS_PORT_CONNECTION_REFUSED cpu_to_le32(0xC0000041) +#define STATUS_INVALID_PORT_HANDLE cpu_to_le32(0xC0000042) +#define STATUS_SHARING_VIOLATION cpu_to_le32(0xC0000043) +#define STATUS_QUOTA_EXCEEDED cpu_to_le32(0xC0000044) +#define STATUS_INVALID_PAGE_PROTECTION cpu_to_le32(0xC0000045) +#define STATUS_MUTANT_NOT_OWNED cpu_to_le32(0xC0000046) +#define STATUS_SEMAPHORE_LIMIT_EXCEEDED cpu_to_le32(0xC0000047) +#define STATUS_PORT_ALREADY_SET cpu_to_le32(0xC0000048) +#define STATUS_SECTION_NOT_IMAGE cpu_to_le32(0xC0000049) +#define STATUS_SUSPEND_COUNT_EXCEEDED cpu_to_le32(0xC000004A) +#define STATUS_THREAD_IS_TERMINATING cpu_to_le32(0xC000004B) +#define STATUS_BAD_WORKING_SET_LIMIT cpu_to_le32(0xC000004C) +#define STATUS_INCOMPATIBLE_FILE_MAP cpu_to_le32(0xC000004D) +#define STATUS_SECTION_PROTECTION cpu_to_le32(0xC000004E) +#define STATUS_EAS_NOT_SUPPORTED cpu_to_le32(0xC000004F) +#define STATUS_EA_TOO_LARGE cpu_to_le32(0xC0000050) +#define STATUS_NONEXISTENT_EA_ENTRY cpu_to_le32(0xC0000051) +#define STATUS_NO_EAS_ON_FILE cpu_to_le32(0xC0000052) +#define STATUS_EA_CORRUPT_ERROR cpu_to_le32(0xC0000053) +#define STATUS_FILE_LOCK_CONFLICT cpu_to_le32(0xC0000054) +#define STATUS_LOCK_NOT_GRANTED cpu_to_le32(0xC0000055) +#define STATUS_DELETE_PENDING cpu_to_le32(0xC0000056) +#define STATUS_CTL_FILE_NOT_SUPPORTED cpu_to_le32(0xC0000057) +#define STATUS_UNKNOWN_REVISION cpu_to_le32(0xC0000058) +#define STATUS_REVISION_MISMATCH cpu_to_le32(0xC0000059) +#define STATUS_INVALID_OWNER cpu_to_le32(0xC000005A) +#define STATUS_INVALID_PRIMARY_GROUP cpu_to_le32(0xC000005B) +#define STATUS_NO_IMPERSONATION_TOKEN cpu_to_le32(0xC000005C) +#define STATUS_CANT_DISABLE_MANDATORY cpu_to_le32(0xC000005D) +#define STATUS_NO_LOGON_SERVERS cpu_to_le32(0xC000005E) +#define STATUS_NO_SUCH_LOGON_SESSION cpu_to_le32(0xC000005F) +#define STATUS_NO_SUCH_PRIVILEGE cpu_to_le32(0xC0000060) +#define STATUS_PRIVILEGE_NOT_HELD cpu_to_le32(0xC0000061) +#define STATUS_INVALID_ACCOUNT_NAME cpu_to_le32(0xC0000062) +#define STATUS_USER_EXISTS cpu_to_le32(0xC0000063) +#define STATUS_NO_SUCH_USER cpu_to_le32(0xC0000064) +#define STATUS_GROUP_EXISTS cpu_to_le32(0xC0000065) +#define STATUS_NO_SUCH_GROUP cpu_to_le32(0xC0000066) +#define STATUS_MEMBER_IN_GROUP cpu_to_le32(0xC0000067) +#define STATUS_MEMBER_NOT_IN_GROUP cpu_to_le32(0xC0000068) +#define STATUS_LAST_ADMIN cpu_to_le32(0xC0000069) +#define STATUS_WRONG_PASSWORD cpu_to_le32(0xC000006A) +#define STATUS_ILL_FORMED_PASSWORD cpu_to_le32(0xC000006B) +#define STATUS_PASSWORD_RESTRICTION cpu_to_le32(0xC000006C) +#define STATUS_LOGON_FAILURE cpu_to_le32(0xC000006D) +#define STATUS_ACCOUNT_RESTRICTION cpu_to_le32(0xC000006E) +#define STATUS_INVALID_LOGON_HOURS cpu_to_le32(0xC000006F) +#define STATUS_INVALID_WORKSTATION cpu_to_le32(0xC0000070) +#define STATUS_PASSWORD_EXPIRED cpu_to_le32(0xC0000071) +#define STATUS_ACCOUNT_DISABLED cpu_to_le32(0xC0000072) +#define STATUS_NONE_MAPPED cpu_to_le32(0xC0000073) +#define STATUS_TOO_MANY_LUIDS_REQUESTED cpu_to_le32(0xC0000074) +#define STATUS_LUIDS_EXHAUSTED cpu_to_le32(0xC0000075) +#define STATUS_INVALID_SUB_AUTHORITY cpu_to_le32(0xC0000076) +#define STATUS_INVALID_ACL cpu_to_le32(0xC0000077) +#define STATUS_INVALID_SID cpu_to_le32(0xC0000078) +#define STATUS_INVALID_SECURITY_DESCR cpu_to_le32(0xC0000079) +#define STATUS_PROCEDURE_NOT_FOUND cpu_to_le32(0xC000007A) +#define STATUS_INVALID_IMAGE_FORMAT cpu_to_le32(0xC000007B) +#define STATUS_NO_TOKEN cpu_to_le32(0xC000007C) +#define STATUS_BAD_INHERITANCE_ACL cpu_to_le32(0xC000007D) +#define STATUS_RANGE_NOT_LOCKED cpu_to_le32(0xC000007E) +#define STATUS_DISK_FULL cpu_to_le32(0xC000007F) +#define STATUS_SERVER_DISABLED cpu_to_le32(0xC0000080) +#define STATUS_SERVER_NOT_DISABLED cpu_to_le32(0xC0000081) +#define STATUS_TOO_MANY_GUIDS_REQUESTED cpu_to_le32(0xC0000082) +#define STATUS_GUIDS_EXHAUSTED cpu_to_le32(0xC0000083) +#define STATUS_INVALID_ID_AUTHORITY cpu_to_le32(0xC0000084) +#define STATUS_AGENTS_EXHAUSTED cpu_to_le32(0xC0000085) +#define STATUS_INVALID_VOLUME_LABEL cpu_to_le32(0xC0000086) +#define STATUS_SECTION_NOT_EXTENDED cpu_to_le32(0xC0000087) +#define STATUS_NOT_MAPPED_DATA cpu_to_le32(0xC0000088) +#define STATUS_RESOURCE_DATA_NOT_FOUND cpu_to_le32(0xC0000089) +#define STATUS_RESOURCE_TYPE_NOT_FOUND cpu_to_le32(0xC000008A) +#define STATUS_RESOURCE_NAME_NOT_FOUND cpu_to_le32(0xC000008B) +#define STATUS_ARRAY_BOUNDS_EXCEEDED cpu_to_le32(0xC000008C) +#define STATUS_FLOAT_DENORMAL_OPERAND cpu_to_le32(0xC000008D) +#define STATUS_FLOAT_DIVIDE_BY_ZERO cpu_to_le32(0xC000008E) +#define STATUS_FLOAT_INEXACT_RESULT cpu_to_le32(0xC000008F) +#define STATUS_FLOAT_INVALID_OPERATION cpu_to_le32(0xC0000090) +#define STATUS_FLOAT_OVERFLOW cpu_to_le32(0xC0000091) +#define STATUS_FLOAT_STACK_CHECK cpu_to_le32(0xC0000092) +#define STATUS_FLOAT_UNDERFLOW cpu_to_le32(0xC0000093) +#define STATUS_INTEGER_DIVIDE_BY_ZERO cpu_to_le32(0xC0000094) +#define STATUS_INTEGER_OVERFLOW cpu_to_le32(0xC0000095) +#define STATUS_PRIVILEGED_INSTRUCTION cpu_to_le32(0xC0000096) +#define STATUS_TOO_MANY_PAGING_FILES cpu_to_le32(0xC0000097) +#define STATUS_FILE_INVALID cpu_to_le32(0xC0000098) +#define STATUS_ALLOTTED_SPACE_EXCEEDED cpu_to_le32(0xC0000099) +#define STATUS_INSUFFICIENT_RESOURCES cpu_to_le32(0xC000009A) +#define STATUS_DFS_EXIT_PATH_FOUND cpu_to_le32(0xC000009B) +#define STATUS_DEVICE_DATA_ERROR cpu_to_le32(0xC000009C) +#define STATUS_DEVICE_NOT_CONNECTED cpu_to_le32(0xC000009D) +#define STATUS_DEVICE_POWER_FAILURE cpu_to_le32(0xC000009E) +#define STATUS_FREE_VM_NOT_AT_BASE cpu_to_le32(0xC000009F) +#define STATUS_MEMORY_NOT_ALLOCATED cpu_to_le32(0xC00000A0) +#define STATUS_WORKING_SET_QUOTA cpu_to_le32(0xC00000A1) +#define STATUS_MEDIA_WRITE_PROTECTED cpu_to_le32(0xC00000A2) +#define STATUS_DEVICE_NOT_READY cpu_to_le32(0xC00000A3) +#define STATUS_INVALID_GROUP_ATTRIBUTES cpu_to_le32(0xC00000A4) +#define STATUS_BAD_IMPERSONATION_LEVEL cpu_to_le32(0xC00000A5) +#define STATUS_CANT_OPEN_ANONYMOUS cpu_to_le32(0xC00000A6) +#define STATUS_BAD_VALIDATION_CLASS cpu_to_le32(0xC00000A7) +#define STATUS_BAD_TOKEN_TYPE cpu_to_le32(0xC00000A8) +#define STATUS_BAD_MASTER_BOOT_RECORD cpu_to_le32(0xC00000A9) +#define STATUS_INSTRUCTION_MISALIGNMENT cpu_to_le32(0xC00000AA) +#define STATUS_INSTANCE_NOT_AVAILABLE cpu_to_le32(0xC00000AB) +#define STATUS_PIPE_NOT_AVAILABLE cpu_to_le32(0xC00000AC) +#define STATUS_INVALID_PIPE_STATE cpu_to_le32(0xC00000AD) +#define STATUS_PIPE_BUSY cpu_to_le32(0xC00000AE) +#define STATUS_ILLEGAL_FUNCTION cpu_to_le32(0xC00000AF) +#define STATUS_PIPE_DISCONNECTED cpu_to_le32(0xC00000B0) +#define STATUS_PIPE_CLOSING cpu_to_le32(0xC00000B1) +#define STATUS_PIPE_CONNECTED cpu_to_le32(0xC00000B2) +#define STATUS_PIPE_LISTENING cpu_to_le32(0xC00000B3) +#define STATUS_INVALID_READ_MODE cpu_to_le32(0xC00000B4) +#define STATUS_IO_TIMEOUT cpu_to_le32(0xC00000B5) +#define STATUS_FILE_FORCED_CLOSED cpu_to_le32(0xC00000B6) +#define STATUS_PROFILING_NOT_STARTED cpu_to_le32(0xC00000B7) +#define STATUS_PROFILING_NOT_STOPPED cpu_to_le32(0xC00000B8) +#define STATUS_COULD_NOT_INTERPRET cpu_to_le32(0xC00000B9) +#define STATUS_FILE_IS_A_DIRECTORY cpu_to_le32(0xC00000BA) +#define STATUS_NOT_SUPPORTED cpu_to_le32(0xC00000BB) +#define STATUS_REMOTE_NOT_LISTENING cpu_to_le32(0xC00000BC) +#define STATUS_DUPLICATE_NAME cpu_to_le32(0xC00000BD) +#define STATUS_BAD_NETWORK_PATH cpu_to_le32(0xC00000BE) +#define STATUS_NETWORK_BUSY cpu_to_le32(0xC00000BF) +#define STATUS_DEVICE_DOES_NOT_EXIST cpu_to_le32(0xC00000C0) +#define STATUS_TOO_MANY_COMMANDS cpu_to_le32(0xC00000C1) +#define STATUS_ADAPTER_HARDWARE_ERROR cpu_to_le32(0xC00000C2) +#define STATUS_INVALID_NETWORK_RESPONSE cpu_to_le32(0xC00000C3) +#define STATUS_UNEXPECTED_NETWORK_ERROR cpu_to_le32(0xC00000C4) +#define STATUS_BAD_REMOTE_ADAPTER cpu_to_le32(0xC00000C5) +#define STATUS_PRINT_QUEUE_FULL cpu_to_le32(0xC00000C6) +#define STATUS_NO_SPOOL_SPACE cpu_to_le32(0xC00000C7) +#define STATUS_PRINT_CANCELLED cpu_to_le32(0xC00000C8) +#define STATUS_NETWORK_NAME_DELETED cpu_to_le32(0xC00000C9) +#define STATUS_NETWORK_ACCESS_DENIED cpu_to_le32(0xC00000CA) +#define STATUS_BAD_DEVICE_TYPE cpu_to_le32(0xC00000CB) +#define STATUS_BAD_NETWORK_NAME cpu_to_le32(0xC00000CC) +#define STATUS_TOO_MANY_NAMES cpu_to_le32(0xC00000CD) +#define STATUS_TOO_MANY_SESSIONS cpu_to_le32(0xC00000CE) +#define STATUS_SHARING_PAUSED cpu_to_le32(0xC00000CF) +#define STATUS_REQUEST_NOT_ACCEPTED cpu_to_le32(0xC00000D0) +#define STATUS_REDIRECTOR_PAUSED cpu_to_le32(0xC00000D1) +#define STATUS_NET_WRITE_FAULT cpu_to_le32(0xC00000D2) +#define STATUS_PROFILING_AT_LIMIT cpu_to_le32(0xC00000D3) +#define STATUS_NOT_SAME_DEVICE cpu_to_le32(0xC00000D4) +#define STATUS_FILE_RENAMED cpu_to_le32(0xC00000D5) +#define STATUS_VIRTUAL_CIRCUIT_CLOSED cpu_to_le32(0xC00000D6) +#define STATUS_NO_SECURITY_ON_OBJECT cpu_to_le32(0xC00000D7) +#define STATUS_CANT_WAIT cpu_to_le32(0xC00000D8) +#define STATUS_PIPE_EMPTY cpu_to_le32(0xC00000D9) +#define STATUS_CANT_ACCESS_DOMAIN_INFO cpu_to_le32(0xC00000DA) +#define STATUS_CANT_TERMINATE_SELF cpu_to_le32(0xC00000DB) +#define STATUS_INVALID_SERVER_STATE cpu_to_le32(0xC00000DC) +#define STATUS_INVALID_DOMAIN_STATE cpu_to_le32(0xC00000DD) +#define STATUS_INVALID_DOMAIN_ROLE cpu_to_le32(0xC00000DE) +#define STATUS_NO_SUCH_DOMAIN cpu_to_le32(0xC00000DF) +#define STATUS_DOMAIN_EXISTS cpu_to_le32(0xC00000E0) +#define STATUS_DOMAIN_LIMIT_EXCEEDED cpu_to_le32(0xC00000E1) +#define STATUS_OPLOCK_NOT_GRANTED cpu_to_le32(0xC00000E2) +#define STATUS_INVALID_OPLOCK_PROTOCOL cpu_to_le32(0xC00000E3) +#define STATUS_INTERNAL_DB_CORRUPTION cpu_to_le32(0xC00000E4) +#define STATUS_INTERNAL_ERROR cpu_to_le32(0xC00000E5) +#define STATUS_GENERIC_NOT_MAPPED cpu_to_le32(0xC00000E6) +#define STATUS_BAD_DESCRIPTOR_FORMAT cpu_to_le32(0xC00000E7) +#define STATUS_INVALID_USER_BUFFER cpu_to_le32(0xC00000E8) +#define STATUS_UNEXPECTED_IO_ERROR cpu_to_le32(0xC00000E9) +#define STATUS_UNEXPECTED_MM_CREATE_ERR cpu_to_le32(0xC00000EA) +#define STATUS_UNEXPECTED_MM_MAP_ERROR cpu_to_le32(0xC00000EB) +#define STATUS_UNEXPECTED_MM_EXTEND_ERR cpu_to_le32(0xC00000EC) +#define STATUS_NOT_LOGON_PROCESS cpu_to_le32(0xC00000ED) +#define STATUS_LOGON_SESSION_EXISTS cpu_to_le32(0xC00000EE) +#define STATUS_INVALID_PARAMETER_1 cpu_to_le32(0xC00000EF) +#define STATUS_INVALID_PARAMETER_2 cpu_to_le32(0xC00000F0) +#define STATUS_INVALID_PARAMETER_3 cpu_to_le32(0xC00000F1) +#define STATUS_INVALID_PARAMETER_4 cpu_to_le32(0xC00000F2) +#define STATUS_INVALID_PARAMETER_5 cpu_to_le32(0xC00000F3) +#define STATUS_INVALID_PARAMETER_6 cpu_to_le32(0xC00000F4) +#define STATUS_INVALID_PARAMETER_7 cpu_to_le32(0xC00000F5) +#define STATUS_INVALID_PARAMETER_8 cpu_to_le32(0xC00000F6) +#define STATUS_INVALID_PARAMETER_9 cpu_to_le32(0xC00000F7) +#define STATUS_INVALID_PARAMETER_10 cpu_to_le32(0xC00000F8) +#define STATUS_INVALID_PARAMETER_11 cpu_to_le32(0xC00000F9) +#define STATUS_INVALID_PARAMETER_12 cpu_to_le32(0xC00000FA) +#define STATUS_REDIRECTOR_NOT_STARTED cpu_to_le32(0xC00000FB) +#define STATUS_REDIRECTOR_STARTED cpu_to_le32(0xC00000FC) +#define STATUS_STACK_OVERFLOW cpu_to_le32(0xC00000FD) +#define STATUS_NO_SUCH_PACKAGE cpu_to_le32(0xC00000FE) +#define STATUS_BAD_FUNCTION_TABLE cpu_to_le32(0xC00000FF) +#define STATUS_VARIABLE_NOT_FOUND cpu_to_le32(0xC0000100) +#define STATUS_DIRECTORY_NOT_EMPTY cpu_to_le32(0xC0000101) +#define STATUS_FILE_CORRUPT_ERROR cpu_to_le32(0xC0000102) +#define STATUS_NOT_A_DIRECTORY cpu_to_le32(0xC0000103) +#define STATUS_BAD_LOGON_SESSION_STATE cpu_to_le32(0xC0000104) +#define STATUS_LOGON_SESSION_COLLISION cpu_to_le32(0xC0000105) +#define STATUS_NAME_TOO_LONG cpu_to_le32(0xC0000106) +#define STATUS_FILES_OPEN cpu_to_le32(0xC0000107) +#define STATUS_CONNECTION_IN_USE cpu_to_le32(0xC0000108) +#define STATUS_MESSAGE_NOT_FOUND cpu_to_le32(0xC0000109) +#define STATUS_PROCESS_IS_TERMINATING cpu_to_le32(0xC000010A) +#define STATUS_INVALID_LOGON_TYPE cpu_to_le32(0xC000010B) +#define STATUS_NO_GUID_TRANSLATION cpu_to_le32(0xC000010C) +#define STATUS_CANNOT_IMPERSONATE cpu_to_le32(0xC000010D) +#define STATUS_IMAGE_ALREADY_LOADED cpu_to_le32(0xC000010E) +#define STATUS_ABIOS_NOT_PRESENT cpu_to_le32(0xC000010F) +#define STATUS_ABIOS_LID_NOT_EXIST cpu_to_le32(0xC0000110) +#define STATUS_ABIOS_LID_ALREADY_OWNED cpu_to_le32(0xC0000111) +#define STATUS_ABIOS_NOT_LID_OWNER cpu_to_le32(0xC0000112) +#define STATUS_ABIOS_INVALID_COMMAND cpu_to_le32(0xC0000113) +#define STATUS_ABIOS_INVALID_LID cpu_to_le32(0xC0000114) +#define STATUS_ABIOS_SELECTOR_NOT_AVAILABLE cpu_to_le32(0xC0000115) +#define STATUS_ABIOS_INVALID_SELECTOR cpu_to_le32(0xC0000116) +#define STATUS_NO_LDT cpu_to_le32(0xC0000117) +#define STATUS_INVALID_LDT_SIZE cpu_to_le32(0xC0000118) +#define STATUS_INVALID_LDT_OFFSET cpu_to_le32(0xC0000119) +#define STATUS_INVALID_LDT_DESCRIPTOR cpu_to_le32(0xC000011A) +#define STATUS_INVALID_IMAGE_NE_FORMAT cpu_to_le32(0xC000011B) +#define STATUS_RXACT_INVALID_STATE cpu_to_le32(0xC000011C) +#define STATUS_RXACT_COMMIT_FAILURE cpu_to_le32(0xC000011D) +#define STATUS_MAPPED_FILE_SIZE_ZERO cpu_to_le32(0xC000011E) +#define STATUS_TOO_MANY_OPENED_FILES cpu_to_le32(0xC000011F) +#define STATUS_CANCELLED cpu_to_le32(0xC0000120) +#define STATUS_CANNOT_DELETE cpu_to_le32(0xC0000121) +#define STATUS_INVALID_COMPUTER_NAME cpu_to_le32(0xC0000122) +#define STATUS_FILE_DELETED cpu_to_le32(0xC0000123) +#define STATUS_SPECIAL_ACCOUNT cpu_to_le32(0xC0000124) +#define STATUS_SPECIAL_GROUP cpu_to_le32(0xC0000125) +#define STATUS_SPECIAL_USER cpu_to_le32(0xC0000126) +#define STATUS_MEMBERS_PRIMARY_GROUP cpu_to_le32(0xC0000127) +#define STATUS_FILE_CLOSED cpu_to_le32(0xC0000128) +#define STATUS_TOO_MANY_THREADS cpu_to_le32(0xC0000129) +#define STATUS_THREAD_NOT_IN_PROCESS cpu_to_le32(0xC000012A) +#define STATUS_TOKEN_ALREADY_IN_USE cpu_to_le32(0xC000012B) +#define STATUS_PAGEFILE_QUOTA_EXCEEDED cpu_to_le32(0xC000012C) +#define STATUS_COMMITMENT_LIMIT cpu_to_le32(0xC000012D) +#define STATUS_INVALID_IMAGE_LE_FORMAT cpu_to_le32(0xC000012E) +#define STATUS_INVALID_IMAGE_NOT_MZ cpu_to_le32(0xC000012F) +#define STATUS_INVALID_IMAGE_PROTECT cpu_to_le32(0xC0000130) +#define STATUS_INVALID_IMAGE_WIN_16 cpu_to_le32(0xC0000131) +#define STATUS_LOGON_SERVER_CONFLICT cpu_to_le32(0xC0000132) +#define STATUS_TIME_DIFFERENCE_AT_DC cpu_to_le32(0xC0000133) +#define STATUS_SYNCHRONIZATION_REQUIRED cpu_to_le32(0xC0000134) +#define STATUS_DLL_NOT_FOUND cpu_to_le32(0xC0000135) +#define STATUS_OPEN_FAILED cpu_to_le32(0xC0000136) +#define STATUS_IO_PRIVILEGE_FAILED cpu_to_le32(0xC0000137) +#define STATUS_ORDINAL_NOT_FOUND cpu_to_le32(0xC0000138) +#define STATUS_ENTRYPOINT_NOT_FOUND cpu_to_le32(0xC0000139) +#define STATUS_CONTROL_C_EXIT cpu_to_le32(0xC000013A) +#define STATUS_LOCAL_DISCONNECT cpu_to_le32(0xC000013B) +#define STATUS_REMOTE_DISCONNECT cpu_to_le32(0xC000013C) +#define STATUS_REMOTE_RESOURCES cpu_to_le32(0xC000013D) +#define STATUS_LINK_FAILED cpu_to_le32(0xC000013E) +#define STATUS_LINK_TIMEOUT cpu_to_le32(0xC000013F) +#define STATUS_INVALID_CONNECTION cpu_to_le32(0xC0000140) +#define STATUS_INVALID_ADDRESS cpu_to_le32(0xC0000141) +#define STATUS_DLL_INIT_FAILED cpu_to_le32(0xC0000142) +#define STATUS_MISSING_SYSTEMFILE cpu_to_le32(0xC0000143) +#define STATUS_UNHANDLED_EXCEPTION cpu_to_le32(0xC0000144) +#define STATUS_APP_INIT_FAILURE cpu_to_le32(0xC0000145) +#define STATUS_PAGEFILE_CREATE_FAILED cpu_to_le32(0xC0000146) +#define STATUS_NO_PAGEFILE cpu_to_le32(0xC0000147) +#define STATUS_INVALID_LEVEL cpu_to_le32(0xC0000148) +#define STATUS_WRONG_PASSWORD_CORE cpu_to_le32(0xC0000149) +#define STATUS_ILLEGAL_FLOAT_CONTEXT cpu_to_le32(0xC000014A) +#define STATUS_PIPE_BROKEN cpu_to_le32(0xC000014B) +#define STATUS_REGISTRY_CORRUPT cpu_to_le32(0xC000014C) +#define STATUS_REGISTRY_IO_FAILED cpu_to_le32(0xC000014D) +#define STATUS_NO_EVENT_PAIR cpu_to_le32(0xC000014E) +#define STATUS_UNRECOGNIZED_VOLUME cpu_to_le32(0xC000014F) +#define STATUS_SERIAL_NO_DEVICE_INITED cpu_to_le32(0xC0000150) +#define STATUS_NO_SUCH_ALIAS cpu_to_le32(0xC0000151) +#define STATUS_MEMBER_NOT_IN_ALIAS cpu_to_le32(0xC0000152) +#define STATUS_MEMBER_IN_ALIAS cpu_to_le32(0xC0000153) +#define STATUS_ALIAS_EXISTS cpu_to_le32(0xC0000154) +#define STATUS_LOGON_NOT_GRANTED cpu_to_le32(0xC0000155) +#define STATUS_TOO_MANY_SECRETS cpu_to_le32(0xC0000156) +#define STATUS_SECRET_TOO_LONG cpu_to_le32(0xC0000157) +#define STATUS_INTERNAL_DB_ERROR cpu_to_le32(0xC0000158) +#define STATUS_FULLSCREEN_MODE cpu_to_le32(0xC0000159) +#define STATUS_TOO_MANY_CONTEXT_IDS cpu_to_le32(0xC000015A) +#define STATUS_LOGON_TYPE_NOT_GRANTED cpu_to_le32(0xC000015B) +#define STATUS_NOT_REGISTRY_FILE cpu_to_le32(0xC000015C) +#define STATUS_NT_CROSS_ENCRYPTION_REQUIRED cpu_to_le32(0xC000015D) +#define STATUS_DOMAIN_CTRLR_CONFIG_ERROR cpu_to_le32(0xC000015E) +#define STATUS_FT_MISSING_MEMBER cpu_to_le32(0xC000015F) +#define STATUS_ILL_FORMED_SERVICE_ENTRY cpu_to_le32(0xC0000160) +#define STATUS_ILLEGAL_CHARACTER cpu_to_le32(0xC0000161) +#define STATUS_UNMAPPABLE_CHARACTER cpu_to_le32(0xC0000162) +#define STATUS_UNDEFINED_CHARACTER cpu_to_le32(0xC0000163) +#define STATUS_FLOPPY_VOLUME cpu_to_le32(0xC0000164) +#define STATUS_FLOPPY_ID_MARK_NOT_FOUND cpu_to_le32(0xC0000165) +#define STATUS_FLOPPY_WRONG_CYLINDER cpu_to_le32(0xC0000166) +#define STATUS_FLOPPY_UNKNOWN_ERROR cpu_to_le32(0xC0000167) +#define STATUS_FLOPPY_BAD_REGISTERS cpu_to_le32(0xC0000168) +#define STATUS_DISK_RECALIBRATE_FAILED cpu_to_le32(0xC0000169) +#define STATUS_DISK_OPERATION_FAILED cpu_to_le32(0xC000016A) +#define STATUS_DISK_RESET_FAILED cpu_to_le32(0xC000016B) +#define STATUS_SHARED_IRQ_BUSY cpu_to_le32(0xC000016C) +#define STATUS_FT_ORPHANING cpu_to_le32(0xC000016D) +#define STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT cpu_to_le32(0xC000016E) +#define STATUS_PARTITION_FAILURE cpu_to_le32(0xC0000172) +#define STATUS_INVALID_BLOCK_LENGTH cpu_to_le32(0xC0000173) +#define STATUS_DEVICE_NOT_PARTITIONED cpu_to_le32(0xC0000174) +#define STATUS_UNABLE_TO_LOCK_MEDIA cpu_to_le32(0xC0000175) +#define STATUS_UNABLE_TO_UNLOAD_MEDIA cpu_to_le32(0xC0000176) +#define STATUS_EOM_OVERFLOW cpu_to_le32(0xC0000177) +#define STATUS_NO_MEDIA cpu_to_le32(0xC0000178) +#define STATUS_NO_SUCH_MEMBER cpu_to_le32(0xC000017A) +#define STATUS_INVALID_MEMBER cpu_to_le32(0xC000017B) +#define STATUS_KEY_DELETED cpu_to_le32(0xC000017C) +#define STATUS_NO_LOG_SPACE cpu_to_le32(0xC000017D) +#define STATUS_TOO_MANY_SIDS cpu_to_le32(0xC000017E) +#define STATUS_LM_CROSS_ENCRYPTION_REQUIRED cpu_to_le32(0xC000017F) +#define STATUS_KEY_HAS_CHILDREN cpu_to_le32(0xC0000180) +#define STATUS_CHILD_MUST_BE_VOLATILE cpu_to_le32(0xC0000181) +#define STATUS_DEVICE_CONFIGURATION_ERROR cpu_to_le32(0xC0000182) +#define STATUS_DRIVER_INTERNAL_ERROR cpu_to_le32(0xC0000183) +#define STATUS_INVALID_DEVICE_STATE cpu_to_le32(0xC0000184) +#define STATUS_IO_DEVICE_ERROR cpu_to_le32(0xC0000185) +#define STATUS_DEVICE_PROTOCOL_ERROR cpu_to_le32(0xC0000186) +#define STATUS_BACKUP_CONTROLLER cpu_to_le32(0xC0000187) +#define STATUS_LOG_FILE_FULL cpu_to_le32(0xC0000188) +#define STATUS_TOO_LATE cpu_to_le32(0xC0000189) +#define STATUS_NO_TRUST_LSA_SECRET cpu_to_le32(0xC000018A) +#define STATUS_NO_TRUST_SAM_ACCOUNT cpu_to_le32(0xC000018B) +#define STATUS_TRUSTED_DOMAIN_FAILURE cpu_to_le32(0xC000018C) +#define STATUS_TRUSTED_RELATIONSHIP_FAILURE cpu_to_le32(0xC000018D) +#define STATUS_EVENTLOG_FILE_CORRUPT cpu_to_le32(0xC000018E) +#define STATUS_EVENTLOG_CANT_START cpu_to_le32(0xC000018F) +#define STATUS_TRUST_FAILURE cpu_to_le32(0xC0000190) +#define STATUS_MUTANT_LIMIT_EXCEEDED cpu_to_le32(0xC0000191) +#define STATUS_NETLOGON_NOT_STARTED cpu_to_le32(0xC0000192) +#define STATUS_ACCOUNT_EXPIRED cpu_to_le32(0xC0000193) +#define STATUS_POSSIBLE_DEADLOCK cpu_to_le32(0xC0000194) +#define STATUS_NETWORK_CREDENTIAL_CONFLICT cpu_to_le32(0xC0000195) +#define STATUS_REMOTE_SESSION_LIMIT cpu_to_le32(0xC0000196) +#define STATUS_EVENTLOG_FILE_CHANGED cpu_to_le32(0xC0000197) +#define STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT cpu_to_le32(0xC0000198) +#define STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT cpu_to_le32(0xC0000199) +#define STATUS_NOLOGON_SERVER_TRUST_ACCOUNT cpu_to_le32(0xC000019A) +#define STATUS_DOMAIN_TRUST_INCONSISTENT cpu_to_le32(0xC000019B) +#define STATUS_FS_DRIVER_REQUIRED cpu_to_le32(0xC000019C) +#define STATUS_IMAGE_ALREADY_LOADED_AS_DLL cpu_to_le32(0xC000019D) +#define STATUS_NETWORK_OPEN_RESTRICTION cpu_to_le32(0xC0000201) +#define STATUS_NO_USER_SESSION_KEY cpu_to_le32(0xC0000202) +#define STATUS_USER_SESSION_DELETED cpu_to_le32(0xC0000203) +#define STATUS_RESOURCE_LANG_NOT_FOUND cpu_to_le32(0xC0000204) +#define STATUS_INSUFF_SERVER_RESOURCES cpu_to_le32(0xC0000205) +#define STATUS_INVALID_BUFFER_SIZE cpu_to_le32(0xC0000206) +#define STATUS_INVALID_ADDRESS_COMPONENT cpu_to_le32(0xC0000207) +#define STATUS_INVALID_ADDRESS_WILDCARD cpu_to_le32(0xC0000208) +#define STATUS_TOO_MANY_ADDRESSES cpu_to_le32(0xC0000209) +#define STATUS_ADDRESS_ALREADY_EXISTS cpu_to_le32(0xC000020A) +#define STATUS_ADDRESS_CLOSED cpu_to_le32(0xC000020B) +#define STATUS_CONNECTION_DISCONNECTED cpu_to_le32(0xC000020C) +#define STATUS_CONNECTION_RESET cpu_to_le32(0xC000020D) +#define STATUS_TOO_MANY_NODES cpu_to_le32(0xC000020E) +#define STATUS_TRANSACTION_ABORTED cpu_to_le32(0xC000020F) +#define STATUS_TRANSACTION_TIMED_OUT cpu_to_le32(0xC0000210) +#define STATUS_TRANSACTION_NO_RELEASE cpu_to_le32(0xC0000211) +#define STATUS_TRANSACTION_NO_MATCH cpu_to_le32(0xC0000212) +#define STATUS_TRANSACTION_RESPONDED cpu_to_le32(0xC0000213) +#define STATUS_TRANSACTION_INVALID_ID cpu_to_le32(0xC0000214) +#define STATUS_TRANSACTION_INVALID_TYPE cpu_to_le32(0xC0000215) +#define STATUS_NOT_SERVER_SESSION cpu_to_le32(0xC0000216) +#define STATUS_NOT_CLIENT_SESSION cpu_to_le32(0xC0000217) +#define STATUS_CANNOT_LOAD_REGISTRY_FILE cpu_to_le32(0xC0000218) +#define STATUS_DEBUG_ATTACH_FAILED cpu_to_le32(0xC0000219) +#define STATUS_SYSTEM_PROCESS_TERMINATED cpu_to_le32(0xC000021A) +#define STATUS_DATA_NOT_ACCEPTED cpu_to_le32(0xC000021B) +#define STATUS_NO_BROWSER_SERVERS_FOUND cpu_to_le32(0xC000021C) +#define STATUS_VDM_HARD_ERROR cpu_to_le32(0xC000021D) +#define STATUS_DRIVER_CANCEL_TIMEOUT cpu_to_le32(0xC000021E) +#define STATUS_REPLY_MESSAGE_MISMATCH cpu_to_le32(0xC000021F) +#define STATUS_MAPPED_ALIGNMENT cpu_to_le32(0xC0000220) +#define STATUS_IMAGE_CHECKSUM_MISMATCH cpu_to_le32(0xC0000221) +#define STATUS_LOST_WRITEBEHIND_DATA cpu_to_le32(0xC0000222) +#define STATUS_CLIENT_SERVER_PARAMETERS_INVALID cpu_to_le32(0xC0000223) +#define STATUS_PASSWORD_MUST_CHANGE cpu_to_le32(0xC0000224) +#define STATUS_NOT_FOUND cpu_to_le32(0xC0000225) +#define STATUS_NOT_TINY_STREAM cpu_to_le32(0xC0000226) +#define STATUS_RECOVERY_FAILURE cpu_to_le32(0xC0000227) +#define STATUS_STACK_OVERFLOW_READ cpu_to_le32(0xC0000228) +#define STATUS_FAIL_CHECK cpu_to_le32(0xC0000229) +#define STATUS_DUPLICATE_OBJECTID cpu_to_le32(0xC000022A) +#define STATUS_OBJECTID_EXISTS cpu_to_le32(0xC000022B) +#define STATUS_CONVERT_TO_LARGE cpu_to_le32(0xC000022C) +#define STATUS_RETRY cpu_to_le32(0xC000022D) +#define STATUS_FOUND_OUT_OF_SCOPE cpu_to_le32(0xC000022E) +#define STATUS_ALLOCATE_BUCKET cpu_to_le32(0xC000022F) +#define STATUS_PROPSET_NOT_FOUND cpu_to_le32(0xC0000230) +#define STATUS_MARSHALL_OVERFLOW cpu_to_le32(0xC0000231) +#define STATUS_INVALID_VARIANT cpu_to_le32(0xC0000232) +#define STATUS_DOMAIN_CONTROLLER_NOT_FOUND cpu_to_le32(0xC0000233) +#define STATUS_ACCOUNT_LOCKED_OUT cpu_to_le32(0xC0000234) +#define STATUS_HANDLE_NOT_CLOSABLE cpu_to_le32(0xC0000235) +#define STATUS_CONNECTION_REFUSED cpu_to_le32(0xC0000236) +#define STATUS_GRACEFUL_DISCONNECT cpu_to_le32(0xC0000237) +#define STATUS_ADDRESS_ALREADY_ASSOCIATED cpu_to_le32(0xC0000238) +#define STATUS_ADDRESS_NOT_ASSOCIATED cpu_to_le32(0xC0000239) +#define STATUS_CONNECTION_INVALID cpu_to_le32(0xC000023A) +#define STATUS_CONNECTION_ACTIVE cpu_to_le32(0xC000023B) +#define STATUS_NETWORK_UNREACHABLE cpu_to_le32(0xC000023C) +#define STATUS_HOST_UNREACHABLE cpu_to_le32(0xC000023D) +#define STATUS_PROTOCOL_UNREACHABLE cpu_to_le32(0xC000023E) +#define STATUS_PORT_UNREACHABLE cpu_to_le32(0xC000023F) +#define STATUS_REQUEST_ABORTED cpu_to_le32(0xC0000240) +#define STATUS_CONNECTION_ABORTED cpu_to_le32(0xC0000241) +#define STATUS_BAD_COMPRESSION_BUFFER cpu_to_le32(0xC0000242) +#define STATUS_USER_MAPPED_FILE cpu_to_le32(0xC0000243) +#define STATUS_AUDIT_FAILED cpu_to_le32(0xC0000244) +#define STATUS_TIMER_RESOLUTION_NOT_SET cpu_to_le32(0xC0000245) +#define STATUS_CONNECTION_COUNT_LIMIT cpu_to_le32(0xC0000246) +#define STATUS_LOGIN_TIME_RESTRICTION cpu_to_le32(0xC0000247) +#define STATUS_LOGIN_WKSTA_RESTRICTION cpu_to_le32(0xC0000248) +#define STATUS_IMAGE_MP_UP_MISMATCH cpu_to_le32(0xC0000249) +#define STATUS_INSUFFICIENT_LOGON_INFO cpu_to_le32(0xC0000250) +#define STATUS_BAD_DLL_ENTRYPOINT cpu_to_le32(0xC0000251) +#define STATUS_BAD_SERVICE_ENTRYPOINT cpu_to_le32(0xC0000252) +#define STATUS_LPC_REPLY_LOST cpu_to_le32(0xC0000253) +#define STATUS_IP_ADDRESS_CONFLICT1 cpu_to_le32(0xC0000254) +#define STATUS_IP_ADDRESS_CONFLICT2 cpu_to_le32(0xC0000255) +#define STATUS_REGISTRY_QUOTA_LIMIT cpu_to_le32(0xC0000256) +#define STATUS_PATH_NOT_COVERED cpu_to_le32(0xC0000257) +#define STATUS_NO_CALLBACK_ACTIVE cpu_to_le32(0xC0000258) +#define STATUS_LICENSE_QUOTA_EXCEEDED cpu_to_le32(0xC0000259) +#define STATUS_PWD_TOO_SHORT cpu_to_le32(0xC000025A) +#define STATUS_PWD_TOO_RECENT cpu_to_le32(0xC000025B) +#define STATUS_PWD_HISTORY_CONFLICT cpu_to_le32(0xC000025C) +#define STATUS_PLUGPLAY_NO_DEVICE cpu_to_le32(0xC000025E) +#define STATUS_UNSUPPORTED_COMPRESSION cpu_to_le32(0xC000025F) +#define STATUS_INVALID_HW_PROFILE cpu_to_le32(0xC0000260) +#define STATUS_INVALID_PLUGPLAY_DEVICE_PATH cpu_to_le32(0xC0000261) +#define STATUS_DRIVER_ORDINAL_NOT_FOUND cpu_to_le32(0xC0000262) +#define STATUS_DRIVER_ENTRYPOINT_NOT_FOUND cpu_to_le32(0xC0000263) +#define STATUS_RESOURCE_NOT_OWNED cpu_to_le32(0xC0000264) +#define STATUS_TOO_MANY_LINKS cpu_to_le32(0xC0000265) +#define STATUS_QUOTA_LIST_INCONSISTENT cpu_to_le32(0xC0000266) +#define STATUS_FILE_IS_OFFLINE cpu_to_le32(0xC0000267) +#define STATUS_EVALUATION_EXPIRATION cpu_to_le32(0xC0000268) +#define STATUS_ILLEGAL_DLL_RELOCATION cpu_to_le32(0xC0000269) +#define STATUS_LICENSE_VIOLATION cpu_to_le32(0xC000026A) +#define STATUS_DLL_INIT_FAILED_LOGOFF cpu_to_le32(0xC000026B) +#define STATUS_DRIVER_UNABLE_TO_LOAD cpu_to_le32(0xC000026C) +#define STATUS_DFS_UNAVAILABLE cpu_to_le32(0xC000026D) +#define STATUS_VOLUME_DISMOUNTED cpu_to_le32(0xC000026E) +#define STATUS_WX86_INTERNAL_ERROR cpu_to_le32(0xC000026F) +#define STATUS_WX86_FLOAT_STACK_CHECK cpu_to_le32(0xC0000270) +#define STATUS_VALIDATE_CONTINUE cpu_to_le32(0xC0000271) +#define STATUS_NO_MATCH cpu_to_le32(0xC0000272) +#define STATUS_NO_MORE_MATCHES cpu_to_le32(0xC0000273) +#define STATUS_NOT_A_REPARSE_POINT cpu_to_le32(0xC0000275) +#define STATUS_IO_REPARSE_TAG_INVALID cpu_to_le32(0xC0000276) +#define STATUS_IO_REPARSE_TAG_MISMATCH cpu_to_le32(0xC0000277) +#define STATUS_IO_REPARSE_DATA_INVALID cpu_to_le32(0xC0000278) +#define STATUS_IO_REPARSE_TAG_NOT_HANDLED cpu_to_le32(0xC0000279) +#define STATUS_REPARSE_POINT_NOT_RESOLVED cpu_to_le32(0xC0000280) +#define STATUS_DIRECTORY_IS_A_REPARSE_POINT cpu_to_le32(0xC0000281) +#define STATUS_RANGE_LIST_CONFLICT cpu_to_le32(0xC0000282) +#define STATUS_SOURCE_ELEMENT_EMPTY cpu_to_le32(0xC0000283) +#define STATUS_DESTINATION_ELEMENT_FULL cpu_to_le32(0xC0000284) +#define STATUS_ILLEGAL_ELEMENT_ADDRESS cpu_to_le32(0xC0000285) +#define STATUS_MAGAZINE_NOT_PRESENT cpu_to_le32(0xC0000286) +#define STATUS_REINITIALIZATION_NEEDED cpu_to_le32(0xC0000287) +#define STATUS_ENCRYPTION_FAILED cpu_to_le32(0xC000028A) +#define STATUS_DECRYPTION_FAILED cpu_to_le32(0xC000028B) +#define STATUS_RANGE_NOT_FOUND cpu_to_le32(0xC000028C) +#define STATUS_NO_RECOVERY_POLICY cpu_to_le32(0xC000028D) +#define STATUS_NO_EFS cpu_to_le32(0xC000028E) +#define STATUS_WRONG_EFS cpu_to_le32(0xC000028F) +#define STATUS_NO_USER_KEYS cpu_to_le32(0xC0000290) +#define STATUS_FILE_NOT_ENCRYPTED cpu_to_le32(0xC0000291) +#define STATUS_NOT_EXPORT_FORMAT cpu_to_le32(0xC0000292) +#define STATUS_FILE_ENCRYPTED cpu_to_le32(0xC0000293) +#define STATUS_WMI_GUID_NOT_FOUND cpu_to_le32(0xC0000295) +#define STATUS_WMI_INSTANCE_NOT_FOUND cpu_to_le32(0xC0000296) +#define STATUS_WMI_ITEMID_NOT_FOUND cpu_to_le32(0xC0000297) +#define STATUS_WMI_TRY_AGAIN cpu_to_le32(0xC0000298) +#define STATUS_SHARED_POLICY cpu_to_le32(0xC0000299) +#define STATUS_POLICY_OBJECT_NOT_FOUND cpu_to_le32(0xC000029A) +#define STATUS_POLICY_ONLY_IN_DS cpu_to_le32(0xC000029B) +#define STATUS_VOLUME_NOT_UPGRADED cpu_to_le32(0xC000029C) +#define STATUS_REMOTE_STORAGE_NOT_ACTIVE cpu_to_le32(0xC000029D) +#define STATUS_REMOTE_STORAGE_MEDIA_ERROR cpu_to_le32(0xC000029E) +#define STATUS_NO_TRACKING_SERVICE cpu_to_le32(0xC000029F) +#define STATUS_SERVER_SID_MISMATCH cpu_to_le32(0xC00002A0) +#define STATUS_DS_NO_ATTRIBUTE_OR_VALUE cpu_to_le32(0xC00002A1) +#define STATUS_DS_INVALID_ATTRIBUTE_SYNTAX cpu_to_le32(0xC00002A2) +#define STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED cpu_to_le32(0xC00002A3) +#define STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS cpu_to_le32(0xC00002A4) +#define STATUS_DS_BUSY cpu_to_le32(0xC00002A5) +#define STATUS_DS_UNAVAILABLE cpu_to_le32(0xC00002A6) +#define STATUS_DS_NO_RIDS_ALLOCATED cpu_to_le32(0xC00002A7) +#define STATUS_DS_NO_MORE_RIDS cpu_to_le32(0xC00002A8) +#define STATUS_DS_INCORRECT_ROLE_OWNER cpu_to_le32(0xC00002A9) +#define STATUS_DS_RIDMGR_INIT_ERROR cpu_to_le32(0xC00002AA) +#define STATUS_DS_OBJ_CLASS_VIOLATION cpu_to_le32(0xC00002AB) +#define STATUS_DS_CANT_ON_NON_LEAF cpu_to_le32(0xC00002AC) +#define STATUS_DS_CANT_ON_RDN cpu_to_le32(0xC00002AD) +#define STATUS_DS_CANT_MOD_OBJ_CLASS cpu_to_le32(0xC00002AE) +#define STATUS_DS_CROSS_DOM_MOVE_FAILED cpu_to_le32(0xC00002AF) +#define STATUS_DS_GC_NOT_AVAILABLE cpu_to_le32(0xC00002B0) +#define STATUS_DIRECTORY_SERVICE_REQUIRED cpu_to_le32(0xC00002B1) +#define STATUS_REPARSE_ATTRIBUTE_CONFLICT cpu_to_le32(0xC00002B2) +#define STATUS_CANT_ENABLE_DENY_ONLY cpu_to_le32(0xC00002B3) +#define STATUS_FLOAT_MULTIPLE_FAULTS cpu_to_le32(0xC00002B4) +#define STATUS_FLOAT_MULTIPLE_TRAPS cpu_to_le32(0xC00002B5) +#define STATUS_DEVICE_REMOVED cpu_to_le32(0xC00002B6) +#define STATUS_JOURNAL_DELETE_IN_PROGRESS cpu_to_le32(0xC00002B7) +#define STATUS_JOURNAL_NOT_ACTIVE cpu_to_le32(0xC00002B8) +#define STATUS_NOINTERFACE cpu_to_le32(0xC00002B9) +#define STATUS_DS_ADMIN_LIMIT_EXCEEDED cpu_to_le32(0xC00002C1) +#define STATUS_DRIVER_FAILED_SLEEP cpu_to_le32(0xC00002C2) +#define STATUS_MUTUAL_AUTHENTICATION_FAILED cpu_to_le32(0xC00002C3) +#define STATUS_CORRUPT_SYSTEM_FILE cpu_to_le32(0xC00002C4) +#define STATUS_DATATYPE_MISALIGNMENT_ERROR cpu_to_le32(0xC00002C5) +#define STATUS_WMI_READ_ONLY cpu_to_le32(0xC00002C6) +#define STATUS_WMI_SET_FAILURE cpu_to_le32(0xC00002C7) +#define STATUS_COMMITMENT_MINIMUM cpu_to_le32(0xC00002C8) +#define STATUS_REG_NAT_CONSUMPTION cpu_to_le32(0xC00002C9) +#define STATUS_TRANSPORT_FULL cpu_to_le32(0xC00002CA) +#define STATUS_DS_SAM_INIT_FAILURE cpu_to_le32(0xC00002CB) +#define STATUS_ONLY_IF_CONNECTED cpu_to_le32(0xC00002CC) +#define STATUS_DS_SENSITIVE_GROUP_VIOLATION cpu_to_le32(0xC00002CD) +#define STATUS_PNP_RESTART_ENUMERATION cpu_to_le32(0xC00002CE) +#define STATUS_JOURNAL_ENTRY_DELETED cpu_to_le32(0xC00002CF) +#define STATUS_DS_CANT_MOD_PRIMARYGROUPID cpu_to_le32(0xC00002D0) +#define STATUS_SYSTEM_IMAGE_BAD_SIGNATURE cpu_to_le32(0xC00002D1) +#define STATUS_PNP_REBOOT_REQUIRED cpu_to_le32(0xC00002D2) +#define STATUS_POWER_STATE_INVALID cpu_to_le32(0xC00002D3) +#define STATUS_DS_INVALID_GROUP_TYPE cpu_to_le32(0xC00002D4) +#define STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN cpu_to_le32(0xC00002D5) +#define STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN cpu_to_le32(0xC00002D6) +#define STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER cpu_to_le32(0xC00002D7) +#define STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER cpu_to_le32(0xC00002D8) +#define STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER cpu_to_le32(0xC00002D9) +#define STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER cpu_to_le32(0xC00002DA) +#define STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER \ + cpu_to_le32(0xC00002DB) +#define STATUS_DS_HAVE_PRIMARY_MEMBERS cpu_to_le32(0xC00002DC) +#define STATUS_WMI_NOT_SUPPORTED cpu_to_le32(0xC00002DD) +#define STATUS_INSUFFICIENT_POWER cpu_to_le32(0xC00002DE) +#define STATUS_SAM_NEED_BOOTKEY_PASSWORD cpu_to_le32(0xC00002DF) +#define STATUS_SAM_NEED_BOOTKEY_FLOPPY cpu_to_le32(0xC00002E0) +#define STATUS_DS_CANT_START cpu_to_le32(0xC00002E1) +#define STATUS_DS_INIT_FAILURE cpu_to_le32(0xC00002E2) +#define STATUS_SAM_INIT_FAILURE cpu_to_le32(0xC00002E3) +#define STATUS_DS_GC_REQUIRED cpu_to_le32(0xC00002E4) +#define STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY cpu_to_le32(0xC00002E5) +#define STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS cpu_to_le32(0xC00002E6) +#define STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED cpu_to_le32(0xC00002E7) +#define STATUS_MULTIPLE_FAULT_VIOLATION cpu_to_le32(0xC00002E8) +#define STATUS_CURRENT_DOMAIN_NOT_ALLOWED cpu_to_le32(0xC00002E9) +#define STATUS_CANNOT_MAKE cpu_to_le32(0xC00002EA) +#define STATUS_SYSTEM_SHUTDOWN cpu_to_le32(0xC00002EB) +#define STATUS_DS_INIT_FAILURE_CONSOLE cpu_to_le32(0xC00002EC) +#define STATUS_DS_SAM_INIT_FAILURE_CONSOLE cpu_to_le32(0xC00002ED) +#define STATUS_UNFINISHED_CONTEXT_DELETED cpu_to_le32(0xC00002EE) +#define STATUS_NO_TGT_REPLY cpu_to_le32(0xC00002EF) +#define STATUS_OBJECTID_NOT_FOUND cpu_to_le32(0xC00002F0) +#define STATUS_NO_IP_ADDRESSES cpu_to_le32(0xC00002F1) +#define STATUS_WRONG_CREDENTIAL_HANDLE cpu_to_le32(0xC00002F2) +#define STATUS_CRYPTO_SYSTEM_INVALID cpu_to_le32(0xC00002F3) +#define STATUS_MAX_REFERRALS_EXCEEDED cpu_to_le32(0xC00002F4) +#define STATUS_MUST_BE_KDC cpu_to_le32(0xC00002F5) +#define STATUS_STRONG_CRYPTO_NOT_SUPPORTED cpu_to_le32(0xC00002F6) +#define STATUS_TOO_MANY_PRINCIPALS cpu_to_le32(0xC00002F7) +#define STATUS_NO_PA_DATA cpu_to_le32(0xC00002F8) +#define STATUS_PKINIT_NAME_MISMATCH cpu_to_le32(0xC00002F9) +#define STATUS_SMARTCARD_LOGON_REQUIRED cpu_to_le32(0xC00002FA) +#define STATUS_KDC_INVALID_REQUEST cpu_to_le32(0xC00002FB) +#define STATUS_KDC_UNABLE_TO_REFER cpu_to_le32(0xC00002FC) +#define STATUS_KDC_UNKNOWN_ETYPE cpu_to_le32(0xC00002FD) +#define STATUS_SHUTDOWN_IN_PROGRESS cpu_to_le32(0xC00002FE) +#define STATUS_SERVER_SHUTDOWN_IN_PROGRESS cpu_to_le32(0xC00002FF) +#define STATUS_NOT_SUPPORTED_ON_SBS cpu_to_le32(0xC0000300) +#define STATUS_WMI_GUID_DISCONNECTED cpu_to_le32(0xC0000301) +#define STATUS_WMI_ALREADY_DISABLED cpu_to_le32(0xC0000302) +#define STATUS_WMI_ALREADY_ENABLED cpu_to_le32(0xC0000303) +#define STATUS_MFT_TOO_FRAGMENTED cpu_to_le32(0xC0000304) +#define STATUS_COPY_PROTECTION_FAILURE cpu_to_le32(0xC0000305) +#define STATUS_CSS_AUTHENTICATION_FAILURE cpu_to_le32(0xC0000306) +#define STATUS_CSS_KEY_NOT_PRESENT cpu_to_le32(0xC0000307) +#define STATUS_CSS_KEY_NOT_ESTABLISHED cpu_to_le32(0xC0000308) +#define STATUS_CSS_SCRAMBLED_SECTOR cpu_to_le32(0xC0000309) +#define STATUS_CSS_REGION_MISMATCH cpu_to_le32(0xC000030A) +#define STATUS_CSS_RESETS_EXHAUSTED cpu_to_le32(0xC000030B) +#define STATUS_PKINIT_FAILURE cpu_to_le32(0xC0000320) +#define STATUS_SMARTCARD_SUBSYSTEM_FAILURE cpu_to_le32(0xC0000321) +#define STATUS_NO_KERB_KEY cpu_to_le32(0xC0000322) +#define STATUS_HOST_DOWN cpu_to_le32(0xC0000350) +#define STATUS_UNSUPPORTED_PREAUTH cpu_to_le32(0xC0000351) +#define STATUS_EFS_ALG_BLOB_TOO_BIG cpu_to_le32(0xC0000352) +#define STATUS_PORT_NOT_SET cpu_to_le32(0xC0000353) +#define STATUS_DEBUGGER_INACTIVE cpu_to_le32(0xC0000354) +#define STATUS_DS_VERSION_CHECK_FAILURE cpu_to_le32(0xC0000355) +#define STATUS_AUDITING_DISABLED cpu_to_le32(0xC0000356) +#define STATUS_PRENT4_MACHINE_ACCOUNT cpu_to_le32(0xC0000357) +#define STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER cpu_to_le32(0xC0000358) +#define STATUS_INVALID_IMAGE_WIN_32 cpu_to_le32(0xC0000359) +#define STATUS_INVALID_IMAGE_WIN_64 cpu_to_le32(0xC000035A) +#define STATUS_BAD_BINDINGS cpu_to_le32(0xC000035B) +#define STATUS_NETWORK_SESSION_EXPIRED cpu_to_le32(0xC000035C) +#define STATUS_APPHELP_BLOCK cpu_to_le32(0xC000035D) +#define STATUS_ALL_SIDS_FILTERED cpu_to_le32(0xC000035E) +#define STATUS_NOT_SAFE_MODE_DRIVER cpu_to_le32(0xC000035F) +#define STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT cpu_to_le32(0xC0000361) +#define STATUS_ACCESS_DISABLED_BY_POLICY_PATH cpu_to_le32(0xC0000362) +#define STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER cpu_to_le32(0xC0000363) +#define STATUS_ACCESS_DISABLED_BY_POLICY_OTHER cpu_to_le32(0xC0000364) +#define STATUS_FAILED_DRIVER_ENTRY cpu_to_le32(0xC0000365) +#define STATUS_DEVICE_ENUMERATION_ERROR cpu_to_le32(0xC0000366) +#define STATUS_MOUNT_POINT_NOT_RESOLVED cpu_to_le32(0xC0000368) +#define STATUS_INVALID_DEVICE_OBJECT_PARAMETER cpu_to_le32(0xC0000369) +#define STATUS_MCA_OCCURRED cpu_to_le32(0xC000036A) +#define STATUS_DRIVER_BLOCKED_CRITICAL cpu_to_le32(0xC000036B) +#define STATUS_DRIVER_BLOCKED cpu_to_le32(0xC000036C) +#define STATUS_DRIVER_DATABASE_ERROR cpu_to_le32(0xC000036D) +#define STATUS_SYSTEM_HIVE_TOO_LARGE cpu_to_le32(0xC000036E) +#define STATUS_INVALID_IMPORT_OF_NON_DLL cpu_to_le32(0xC000036F) +#define STATUS_NO_SECRETS cpu_to_le32(0xC0000371) +#define STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY cpu_to_le32(0xC0000372) +#define STATUS_FAILED_STACK_SWITCH cpu_to_le32(0xC0000373) +#define STATUS_HEAP_CORRUPTION cpu_to_le32(0xC0000374) +#define STATUS_SMARTCARD_WRONG_PIN cpu_to_le32(0xC0000380) +#define STATUS_SMARTCARD_CARD_BLOCKED cpu_to_le32(0xC0000381) +#define STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED cpu_to_le32(0xC0000382) +#define STATUS_SMARTCARD_NO_CARD cpu_to_le32(0xC0000383) +#define STATUS_SMARTCARD_NO_KEY_CONTAINER cpu_to_le32(0xC0000384) +#define STATUS_SMARTCARD_NO_CERTIFICATE cpu_to_le32(0xC0000385) +#define STATUS_SMARTCARD_NO_KEYSET cpu_to_le32(0xC0000386) +#define STATUS_SMARTCARD_IO_ERROR cpu_to_le32(0xC0000387) +#define STATUS_DOWNGRADE_DETECTED cpu_to_le32(0xC0000388) +#define STATUS_SMARTCARD_CERT_REVOKED cpu_to_le32(0xC0000389) +#define STATUS_ISSUING_CA_UNTRUSTED cpu_to_le32(0xC000038A) +#define STATUS_REVOCATION_OFFLINE_C cpu_to_le32(0xC000038B) +#define STATUS_PKINIT_CLIENT_FAILURE cpu_to_le32(0xC000038C) +#define STATUS_SMARTCARD_CERT_EXPIRED cpu_to_le32(0xC000038D) +#define STATUS_DRIVER_FAILED_PRIOR_UNLOAD cpu_to_le32(0xC000038E) +#define STATUS_SMARTCARD_SILENT_CONTEXT cpu_to_le32(0xC000038F) +#define STATUS_PER_USER_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000401) +#define STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000402) +#define STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED cpu_to_le32(0xC0000403) +#define STATUS_DS_NAME_NOT_UNIQUE cpu_to_le32(0xC0000404) +#define STATUS_DS_DUPLICATE_ID_FOUND cpu_to_le32(0xC0000405) +#define STATUS_DS_GROUP_CONVERSION_ERROR cpu_to_le32(0xC0000406) +#define STATUS_VOLSNAP_PREPARE_HIBERNATE cpu_to_le32(0xC0000407) +#define STATUS_USER2USER_REQUIRED cpu_to_le32(0xC0000408) +#define STATUS_STACK_BUFFER_OVERRUN cpu_to_le32(0xC0000409) +#define STATUS_NO_S4U_PROT_SUPPORT cpu_to_le32(0xC000040A) +#define STATUS_CROSSREALM_DELEGATION_FAILURE cpu_to_le32(0xC000040B) +#define STATUS_REVOCATION_OFFLINE_KDC cpu_to_le32(0xC000040C) +#define STATUS_ISSUING_CA_UNTRUSTED_KDC cpu_to_le32(0xC000040D) +#define STATUS_KDC_CERT_EXPIRED cpu_to_le32(0xC000040E) +#define STATUS_KDC_CERT_REVOKED cpu_to_le32(0xC000040F) +#define STATUS_PARAMETER_QUOTA_EXCEEDED cpu_to_le32(0xC0000410) +#define STATUS_HIBERNATION_FAILURE cpu_to_le32(0xC0000411) +#define STATUS_DELAY_LOAD_FAILED cpu_to_le32(0xC0000412) +#define STATUS_AUTHENTICATION_FIREWALL_FAILED cpu_to_le32(0xC0000413) +#define STATUS_VDM_DISALLOWED cpu_to_le32(0xC0000414) +#define STATUS_HUNG_DISPLAY_DRIVER_THREAD cpu_to_le32(0xC0000415) +#define STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE \ + cpu_to_le32(0xC0000416) +#define STATUS_INVALID_CRUNTIME_PARAMETER cpu_to_le32(0xC0000417) +#define STATUS_NTLM_BLOCKED cpu_to_le32(0xC0000418) +#define STATUS_ASSERTION_FAILURE cpu_to_le32(0xC0000420) +#define STATUS_VERIFIER_STOP cpu_to_le32(0xC0000421) +#define STATUS_CALLBACK_POP_STACK cpu_to_le32(0xC0000423) +#define STATUS_INCOMPATIBLE_DRIVER_BLOCKED cpu_to_le32(0xC0000424) +#define STATUS_HIVE_UNLOADED cpu_to_le32(0xC0000425) +#define STATUS_COMPRESSION_DISABLED cpu_to_le32(0xC0000426) +#define STATUS_FILE_SYSTEM_LIMITATION cpu_to_le32(0xC0000427) +#define STATUS_INVALID_IMAGE_HASH cpu_to_le32(0xC0000428) +#define STATUS_NOT_CAPABLE cpu_to_le32(0xC0000429) +#define STATUS_REQUEST_OUT_OF_SEQUENCE cpu_to_le32(0xC000042A) +#define STATUS_IMPLEMENTATION_LIMIT cpu_to_le32(0xC000042B) +#define STATUS_ELEVATION_REQUIRED cpu_to_le32(0xC000042C) +#define STATUS_BEYOND_VDL cpu_to_le32(0xC0000432) +#define STATUS_ENCOUNTERED_WRITE_IN_PROGRESS cpu_to_le32(0xC0000433) +#define STATUS_PTE_CHANGED cpu_to_le32(0xC0000434) +#define STATUS_PURGE_FAILED cpu_to_le32(0xC0000435) +#define STATUS_CRED_REQUIRES_CONFIRMATION cpu_to_le32(0xC0000440) +#define STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE cpu_to_le32(0xC0000441) +#define STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER cpu_to_le32(0xC0000442) +#define STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE cpu_to_le32(0xC0000443) +#define STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE cpu_to_le32(0xC0000444) +#define STATUS_CS_ENCRYPTION_FILE_NOT_CSE cpu_to_le32(0xC0000445) +#define STATUS_INVALID_LABEL cpu_to_le32(0xC0000446) +#define STATUS_DRIVER_PROCESS_TERMINATED cpu_to_le32(0xC0000450) +#define STATUS_AMBIGUOUS_SYSTEM_DEVICE cpu_to_le32(0xC0000451) +#define STATUS_SYSTEM_DEVICE_NOT_FOUND cpu_to_le32(0xC0000452) +#define STATUS_RESTART_BOOT_APPLICATION cpu_to_le32(0xC0000453) +#define STATUS_INVALID_TASK_NAME cpu_to_le32(0xC0000500) +#define STATUS_INVALID_TASK_INDEX cpu_to_le32(0xC0000501) +#define STATUS_THREAD_ALREADY_IN_TASK cpu_to_le32(0xC0000502) +#define STATUS_CALLBACK_BYPASS cpu_to_le32(0xC0000503) +#define STATUS_PORT_CLOSED cpu_to_le32(0xC0000700) +#define STATUS_MESSAGE_LOST cpu_to_le32(0xC0000701) +#define STATUS_INVALID_MESSAGE cpu_to_le32(0xC0000702) +#define STATUS_REQUEST_CANCELED cpu_to_le32(0xC0000703) +#define STATUS_RECURSIVE_DISPATCH cpu_to_le32(0xC0000704) +#define STATUS_LPC_RECEIVE_BUFFER_EXPECTED cpu_to_le32(0xC0000705) +#define STATUS_LPC_INVALID_CONNECTION_USAGE cpu_to_le32(0xC0000706) +#define STATUS_LPC_REQUESTS_NOT_ALLOWED cpu_to_le32(0xC0000707) +#define STATUS_RESOURCE_IN_USE cpu_to_le32(0xC0000708) +#define STATUS_HARDWARE_MEMORY_ERROR cpu_to_le32(0xC0000709) +#define STATUS_THREADPOOL_HANDLE_EXCEPTION cpu_to_le32(0xC000070A) +#define STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED cpu_to_le32(0xC000070B) +#define STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED \ + cpu_to_le32(0xC000070C) +#define STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED \ + cpu_to_le32(0xC000070D) +#define STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED \ + cpu_to_le32(0xC000070E) +#define STATUS_THREADPOOL_RELEASED_DURING_OPERATION cpu_to_le32(0xC000070F) +#define STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING cpu_to_le32(0xC0000710) +#define STATUS_APC_RETURNED_WHILE_IMPERSONATING cpu_to_le32(0xC0000711) +#define STATUS_PROCESS_IS_PROTECTED cpu_to_le32(0xC0000712) +#define STATUS_MCA_EXCEPTION cpu_to_le32(0xC0000713) +#define STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE cpu_to_le32(0xC0000714) +#define STATUS_SYMLINK_CLASS_DISABLED cpu_to_le32(0xC0000715) +#define STATUS_INVALID_IDN_NORMALIZATION cpu_to_le32(0xC0000716) +#define STATUS_NO_UNICODE_TRANSLATION cpu_to_le32(0xC0000717) +#define STATUS_ALREADY_REGISTERED cpu_to_le32(0xC0000718) +#define STATUS_CONTEXT_MISMATCH cpu_to_le32(0xC0000719) +#define STATUS_PORT_ALREADY_HAS_COMPLETION_LIST cpu_to_le32(0xC000071A) +#define STATUS_CALLBACK_RETURNED_THREAD_PRIORITY cpu_to_le32(0xC000071B) +#define STATUS_INVALID_THREAD cpu_to_le32(0xC000071C) +#define STATUS_CALLBACK_RETURNED_TRANSACTION cpu_to_le32(0xC000071D) +#define STATUS_CALLBACK_RETURNED_LDR_LOCK cpu_to_le32(0xC000071E) +#define STATUS_CALLBACK_RETURNED_LANG cpu_to_le32(0xC000071F) +#define STATUS_CALLBACK_RETURNED_PRI_BACK cpu_to_le32(0xC0000720) +#define STATUS_CALLBACK_RETURNED_THREAD_AFFINITY cpu_to_le32(0xC0000721) +#define STATUS_DISK_REPAIR_DISABLED cpu_to_le32(0xC0000800) +#define STATUS_DS_DOMAIN_RENAME_IN_PROGRESS cpu_to_le32(0xC0000801) +#define STATUS_DISK_QUOTA_EXCEEDED cpu_to_le32(0xC0000802) +#define STATUS_CONTENT_BLOCKED cpu_to_le32(0xC0000804) +#define STATUS_BAD_CLUSTERS cpu_to_le32(0xC0000805) +#define STATUS_VOLUME_DIRTY cpu_to_le32(0xC0000806) +#define STATUS_FILE_CHECKED_OUT cpu_to_le32(0xC0000901) +#define STATUS_CHECKOUT_REQUIRED cpu_to_le32(0xC0000902) +#define STATUS_BAD_FILE_TYPE cpu_to_le32(0xC0000903) +#define STATUS_FILE_TOO_LARGE cpu_to_le32(0xC0000904) +#define STATUS_FORMS_AUTH_REQUIRED cpu_to_le32(0xC0000905) +#define STATUS_VIRUS_INFECTED cpu_to_le32(0xC0000906) +#define STATUS_VIRUS_DELETED cpu_to_le32(0xC0000907) +#define STATUS_BAD_MCFG_TABLE cpu_to_le32(0xC0000908) +#define STATUS_WOW_ASSERTION cpu_to_le32(0xC0009898) +#define STATUS_INVALID_SIGNATURE cpu_to_le32(0xC000A000) +#define STATUS_HMAC_NOT_SUPPORTED cpu_to_le32(0xC000A001) +#define STATUS_IPSEC_QUEUE_OVERFLOW cpu_to_le32(0xC000A010) +#define STATUS_ND_QUEUE_OVERFLOW cpu_to_le32(0xC000A011) +#define STATUS_HOPLIMIT_EXCEEDED cpu_to_le32(0xC000A012) +#define STATUS_PROTOCOL_NOT_SUPPORTED cpu_to_le32(0xC000A013) +#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED \ + cpu_to_le32(0xC000A080) +#define STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR \ + cpu_to_le32(0xC000A081) +#define STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR cpu_to_le32(0xC000A082) +#define STATUS_XML_PARSE_ERROR cpu_to_le32(0xC000A083) +#define STATUS_XMLDSIG_ERROR cpu_to_le32(0xC000A084) +#define STATUS_WRONG_COMPARTMENT cpu_to_le32(0xC000A085) +#define STATUS_AUTHIP_FAILURE cpu_to_le32(0xC000A086) +#define DBG_NO_STATE_CHANGE cpu_to_le32(0xC0010001) +#define DBG_APP_NOT_IDLE cpu_to_le32(0xC0010002) +#define RPC_NT_INVALID_STRING_BINDING cpu_to_le32(0xC0020001) +#define RPC_NT_WRONG_KIND_OF_BINDING cpu_to_le32(0xC0020002) +#define RPC_NT_INVALID_BINDING cpu_to_le32(0xC0020003) +#define RPC_NT_PROTSEQ_NOT_SUPPORTED cpu_to_le32(0xC0020004) +#define RPC_NT_INVALID_RPC_PROTSEQ cpu_to_le32(0xC0020005) +#define RPC_NT_INVALID_STRING_UUID cpu_to_le32(0xC0020006) +#define RPC_NT_INVALID_ENDPOINT_FORMAT cpu_to_le32(0xC0020007) +#define RPC_NT_INVALID_NET_ADDR cpu_to_le32(0xC0020008) +#define RPC_NT_NO_ENDPOINT_FOUND cpu_to_le32(0xC0020009) +#define RPC_NT_INVALID_TIMEOUT cpu_to_le32(0xC002000A) +#define RPC_NT_OBJECT_NOT_FOUND cpu_to_le32(0xC002000B) +#define RPC_NT_ALREADY_REGISTERED cpu_to_le32(0xC002000C) +#define RPC_NT_TYPE_ALREADY_REGISTERED cpu_to_le32(0xC002000D) +#define RPC_NT_ALREADY_LISTENING cpu_to_le32(0xC002000E) +#define RPC_NT_NO_PROTSEQS_REGISTERED cpu_to_le32(0xC002000F) +#define RPC_NT_NOT_LISTENING cpu_to_le32(0xC0020010) +#define RPC_NT_UNKNOWN_MGR_TYPE cpu_to_le32(0xC0020011) +#define RPC_NT_UNKNOWN_IF cpu_to_le32(0xC0020012) +#define RPC_NT_NO_BINDINGS cpu_to_le32(0xC0020013) +#define RPC_NT_NO_PROTSEQS cpu_to_le32(0xC0020014) +#define RPC_NT_CANT_CREATE_ENDPOINT cpu_to_le32(0xC0020015) +#define RPC_NT_OUT_OF_RESOURCES cpu_to_le32(0xC0020016) +#define RPC_NT_SERVER_UNAVAILABLE cpu_to_le32(0xC0020017) +#define RPC_NT_SERVER_TOO_BUSY cpu_to_le32(0xC0020018) +#define RPC_NT_INVALID_NETWORK_OPTIONS cpu_to_le32(0xC0020019) +#define RPC_NT_NO_CALL_ACTIVE cpu_to_le32(0xC002001A) +#define RPC_NT_CALL_FAILED cpu_to_le32(0xC002001B) +#define RPC_NT_CALL_FAILED_DNE cpu_to_le32(0xC002001C) +#define RPC_NT_PROTOCOL_ERROR cpu_to_le32(0xC002001D) +#define RPC_NT_UNSUPPORTED_TRANS_SYN cpu_to_le32(0xC002001F) +#define RPC_NT_UNSUPPORTED_TYPE cpu_to_le32(0xC0020021) +#define RPC_NT_INVALID_TAG cpu_to_le32(0xC0020022) +#define RPC_NT_INVALID_BOUND cpu_to_le32(0xC0020023) +#define RPC_NT_NO_ENTRY_NAME cpu_to_le32(0xC0020024) +#define RPC_NT_INVALID_NAME_SYNTAX cpu_to_le32(0xC0020025) +#define RPC_NT_UNSUPPORTED_NAME_SYNTAX cpu_to_le32(0xC0020026) +#define RPC_NT_UUID_NO_ADDRESS cpu_to_le32(0xC0020028) +#define RPC_NT_DUPLICATE_ENDPOINT cpu_to_le32(0xC0020029) +#define RPC_NT_UNKNOWN_AUTHN_TYPE cpu_to_le32(0xC002002A) +#define RPC_NT_MAX_CALLS_TOO_SMALL cpu_to_le32(0xC002002B) +#define RPC_NT_STRING_TOO_LONG cpu_to_le32(0xC002002C) +#define RPC_NT_PROTSEQ_NOT_FOUND cpu_to_le32(0xC002002D) +#define RPC_NT_PROCNUM_OUT_OF_RANGE cpu_to_le32(0xC002002E) +#define RPC_NT_BINDING_HAS_NO_AUTH cpu_to_le32(0xC002002F) +#define RPC_NT_UNKNOWN_AUTHN_SERVICE cpu_to_le32(0xC0020030) +#define RPC_NT_UNKNOWN_AUTHN_LEVEL cpu_to_le32(0xC0020031) +#define RPC_NT_INVALID_AUTH_IDENTITY cpu_to_le32(0xC0020032) +#define RPC_NT_UNKNOWN_AUTHZ_SERVICE cpu_to_le32(0xC0020033) +#define EPT_NT_INVALID_ENTRY cpu_to_le32(0xC0020034) +#define EPT_NT_CANT_PERFORM_OP cpu_to_le32(0xC0020035) +#define EPT_NT_NOT_REGISTERED cpu_to_le32(0xC0020036) +#define RPC_NT_NOTHING_TO_EXPORT cpu_to_le32(0xC0020037) +#define RPC_NT_INCOMPLETE_NAME cpu_to_le32(0xC0020038) +#define RPC_NT_INVALID_VERS_OPTION cpu_to_le32(0xC0020039) +#define RPC_NT_NO_MORE_MEMBERS cpu_to_le32(0xC002003A) +#define RPC_NT_NOT_ALL_OBJS_UNEXPORTED cpu_to_le32(0xC002003B) +#define RPC_NT_INTERFACE_NOT_FOUND cpu_to_le32(0xC002003C) +#define RPC_NT_ENTRY_ALREADY_EXISTS cpu_to_le32(0xC002003D) +#define RPC_NT_ENTRY_NOT_FOUND cpu_to_le32(0xC002003E) +#define RPC_NT_NAME_SERVICE_UNAVAILABLE cpu_to_le32(0xC002003F) +#define RPC_NT_INVALID_NAF_ID cpu_to_le32(0xC0020040) +#define RPC_NT_CANNOT_SUPPORT cpu_to_le32(0xC0020041) +#define RPC_NT_NO_CONTEXT_AVAILABLE cpu_to_le32(0xC0020042) +#define RPC_NT_INTERNAL_ERROR cpu_to_le32(0xC0020043) +#define RPC_NT_ZERO_DIVIDE cpu_to_le32(0xC0020044) +#define RPC_NT_ADDRESS_ERROR cpu_to_le32(0xC0020045) +#define RPC_NT_FP_DIV_ZERO cpu_to_le32(0xC0020046) +#define RPC_NT_FP_UNDERFLOW cpu_to_le32(0xC0020047) +#define RPC_NT_FP_OVERFLOW cpu_to_le32(0xC0020048) +#define RPC_NT_CALL_IN_PROGRESS cpu_to_le32(0xC0020049) +#define RPC_NT_NO_MORE_BINDINGS cpu_to_le32(0xC002004A) +#define RPC_NT_GROUP_MEMBER_NOT_FOUND cpu_to_le32(0xC002004B) +#define EPT_NT_CANT_CREATE cpu_to_le32(0xC002004C) +#define RPC_NT_INVALID_OBJECT cpu_to_le32(0xC002004D) +#define RPC_NT_NO_INTERFACES cpu_to_le32(0xC002004F) +#define RPC_NT_CALL_CANCELLED cpu_to_le32(0xC0020050) +#define RPC_NT_BINDING_INCOMPLETE cpu_to_le32(0xC0020051) +#define RPC_NT_COMM_FAILURE cpu_to_le32(0xC0020052) +#define RPC_NT_UNSUPPORTED_AUTHN_LEVEL cpu_to_le32(0xC0020053) +#define RPC_NT_NO_PRINC_NAME cpu_to_le32(0xC0020054) +#define RPC_NT_NOT_RPC_ERROR cpu_to_le32(0xC0020055) +#define RPC_NT_SEC_PKG_ERROR cpu_to_le32(0xC0020057) +#define RPC_NT_NOT_CANCELLED cpu_to_le32(0xC0020058) +#define RPC_NT_INVALID_ASYNC_HANDLE cpu_to_le32(0xC0020062) +#define RPC_NT_INVALID_ASYNC_CALL cpu_to_le32(0xC0020063) +#define RPC_NT_PROXY_ACCESS_DENIED cpu_to_le32(0xC0020064) +#define RPC_NT_NO_MORE_ENTRIES cpu_to_le32(0xC0030001) +#define RPC_NT_SS_CHAR_TRANS_OPEN_FAIL cpu_to_le32(0xC0030002) +#define RPC_NT_SS_CHAR_TRANS_SHORT_FILE cpu_to_le32(0xC0030003) +#define RPC_NT_SS_IN_NULL_CONTEXT cpu_to_le32(0xC0030004) +#define RPC_NT_SS_CONTEXT_MISMATCH cpu_to_le32(0xC0030005) +#define RPC_NT_SS_CONTEXT_DAMAGED cpu_to_le32(0xC0030006) +#define RPC_NT_SS_HANDLES_MISMATCH cpu_to_le32(0xC0030007) +#define RPC_NT_SS_CANNOT_GET_CALL_HANDLE cpu_to_le32(0xC0030008) +#define RPC_NT_NULL_REF_POINTER cpu_to_le32(0xC0030009) +#define RPC_NT_ENUM_VALUE_OUT_OF_RANGE cpu_to_le32(0xC003000A) +#define RPC_NT_BYTE_COUNT_TOO_SMALL cpu_to_le32(0xC003000B) +#define RPC_NT_BAD_STUB_DATA cpu_to_le32(0xC003000C) +#define RPC_NT_INVALID_ES_ACTION cpu_to_le32(0xC0030059) +#define RPC_NT_WRONG_ES_VERSION cpu_to_le32(0xC003005A) +#define RPC_NT_WRONG_STUB_VERSION cpu_to_le32(0xC003005B) +#define RPC_NT_INVALID_PIPE_OBJECT cpu_to_le32(0xC003005C) +#define RPC_NT_INVALID_PIPE_OPERATION cpu_to_le32(0xC003005D) +#define RPC_NT_WRONG_PIPE_VERSION cpu_to_le32(0xC003005E) +#define RPC_NT_PIPE_CLOSED cpu_to_le32(0xC003005F) +#define RPC_NT_PIPE_DISCIPLINE_ERROR cpu_to_le32(0xC0030060) +#define RPC_NT_PIPE_EMPTY cpu_to_le32(0xC0030061) +#define STATUS_PNP_BAD_MPS_TABLE cpu_to_le32(0xC0040035) +#define STATUS_PNP_TRANSLATION_FAILED cpu_to_le32(0xC0040036) +#define STATUS_PNP_IRQ_TRANSLATION_FAILED cpu_to_le32(0xC0040037) +#define STATUS_PNP_INVALID_ID cpu_to_le32(0xC0040038) +#define STATUS_IO_REISSUE_AS_CACHED cpu_to_le32(0xC0040039) +#define STATUS_CTX_WINSTATION_NAME_INVALID cpu_to_le32(0xC00A0001) +#define STATUS_CTX_INVALID_PD cpu_to_le32(0xC00A0002) +#define STATUS_CTX_PD_NOT_FOUND cpu_to_le32(0xC00A0003) +#define STATUS_CTX_CLOSE_PENDING cpu_to_le32(0xC00A0006) +#define STATUS_CTX_NO_OUTBUF cpu_to_le32(0xC00A0007) +#define STATUS_CTX_MODEM_INF_NOT_FOUND cpu_to_le32(0xC00A0008) +#define STATUS_CTX_INVALID_MODEMNAME cpu_to_le32(0xC00A0009) +#define STATUS_CTX_RESPONSE_ERROR cpu_to_le32(0xC00A000A) +#define STATUS_CTX_MODEM_RESPONSE_TIMEOUT cpu_to_le32(0xC00A000B) +#define STATUS_CTX_MODEM_RESPONSE_NO_CARRIER cpu_to_le32(0xC00A000C) +#define STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE cpu_to_le32(0xC00A000D) +#define STATUS_CTX_MODEM_RESPONSE_BUSY cpu_to_le32(0xC00A000E) +#define STATUS_CTX_MODEM_RESPONSE_VOICE cpu_to_le32(0xC00A000F) +#define STATUS_CTX_TD_ERROR cpu_to_le32(0xC00A0010) +#define STATUS_CTX_LICENSE_CLIENT_INVALID cpu_to_le32(0xC00A0012) +#define STATUS_CTX_LICENSE_NOT_AVAILABLE cpu_to_le32(0xC00A0013) +#define STATUS_CTX_LICENSE_EXPIRED cpu_to_le32(0xC00A0014) +#define STATUS_CTX_WINSTATION_NOT_FOUND cpu_to_le32(0xC00A0015) +#define STATUS_CTX_WINSTATION_NAME_COLLISION cpu_to_le32(0xC00A0016) +#define STATUS_CTX_WINSTATION_BUSY cpu_to_le32(0xC00A0017) +#define STATUS_CTX_BAD_VIDEO_MODE cpu_to_le32(0xC00A0018) +#define STATUS_CTX_GRAPHICS_INVALID cpu_to_le32(0xC00A0022) +#define STATUS_CTX_NOT_CONSOLE cpu_to_le32(0xC00A0024) +#define STATUS_CTX_CLIENT_QUERY_TIMEOUT cpu_to_le32(0xC00A0026) +#define STATUS_CTX_CONSOLE_DISCONNECT cpu_to_le32(0xC00A0027) +#define STATUS_CTX_CONSOLE_CONNECT cpu_to_le32(0xC00A0028) +#define STATUS_CTX_SHADOW_DENIED cpu_to_le32(0xC00A002A) +#define STATUS_CTX_WINSTATION_ACCESS_DENIED cpu_to_le32(0xC00A002B) +#define STATUS_CTX_INVALID_WD cpu_to_le32(0xC00A002E) +#define STATUS_CTX_WD_NOT_FOUND cpu_to_le32(0xC00A002F) +#define STATUS_CTX_SHADOW_INVALID cpu_to_le32(0xC00A0030) +#define STATUS_CTX_SHADOW_DISABLED cpu_to_le32(0xC00A0031) +#define STATUS_RDP_PROTOCOL_ERROR cpu_to_le32(0xC00A0032) +#define STATUS_CTX_CLIENT_LICENSE_NOT_SET cpu_to_le32(0xC00A0033) +#define STATUS_CTX_CLIENT_LICENSE_IN_USE cpu_to_le32(0xC00A0034) +#define STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE cpu_to_le32(0xC00A0035) +#define STATUS_CTX_SHADOW_NOT_RUNNING cpu_to_le32(0xC00A0036) +#define STATUS_CTX_LOGON_DISABLED cpu_to_le32(0xC00A0037) +#define STATUS_CTX_SECURITY_LAYER_ERROR cpu_to_le32(0xC00A0038) +#define STATUS_TS_INCOMPATIBLE_SESSIONS cpu_to_le32(0xC00A0039) +#define STATUS_MUI_FILE_NOT_FOUND cpu_to_le32(0xC00B0001) +#define STATUS_MUI_INVALID_FILE cpu_to_le32(0xC00B0002) +#define STATUS_MUI_INVALID_RC_CONFIG cpu_to_le32(0xC00B0003) +#define STATUS_MUI_INVALID_LOCALE_NAME cpu_to_le32(0xC00B0004) +#define STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME cpu_to_le32(0xC00B0005) +#define STATUS_MUI_FILE_NOT_LOADED cpu_to_le32(0xC00B0006) +#define STATUS_RESOURCE_ENUM_USER_STOP cpu_to_le32(0xC00B0007) +#define STATUS_CLUSTER_INVALID_NODE cpu_to_le32(0xC0130001) +#define STATUS_CLUSTER_NODE_EXISTS cpu_to_le32(0xC0130002) +#define STATUS_CLUSTER_JOIN_IN_PROGRESS cpu_to_le32(0xC0130003) +#define STATUS_CLUSTER_NODE_NOT_FOUND cpu_to_le32(0xC0130004) +#define STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND cpu_to_le32(0xC0130005) +#define STATUS_CLUSTER_NETWORK_EXISTS cpu_to_le32(0xC0130006) +#define STATUS_CLUSTER_NETWORK_NOT_FOUND cpu_to_le32(0xC0130007) +#define STATUS_CLUSTER_NETINTERFACE_EXISTS cpu_to_le32(0xC0130008) +#define STATUS_CLUSTER_NETINTERFACE_NOT_FOUND cpu_to_le32(0xC0130009) +#define STATUS_CLUSTER_INVALID_REQUEST cpu_to_le32(0xC013000A) +#define STATUS_CLUSTER_INVALID_NETWORK_PROVIDER cpu_to_le32(0xC013000B) +#define STATUS_CLUSTER_NODE_DOWN cpu_to_le32(0xC013000C) +#define STATUS_CLUSTER_NODE_UNREACHABLE cpu_to_le32(0xC013000D) +#define STATUS_CLUSTER_NODE_NOT_MEMBER cpu_to_le32(0xC013000E) +#define STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS cpu_to_le32(0xC013000F) +#define STATUS_CLUSTER_INVALID_NETWORK cpu_to_le32(0xC0130010) +#define STATUS_CLUSTER_NO_NET_ADAPTERS cpu_to_le32(0xC0130011) +#define STATUS_CLUSTER_NODE_UP cpu_to_le32(0xC0130012) +#define STATUS_CLUSTER_NODE_PAUSED cpu_to_le32(0xC0130013) +#define STATUS_CLUSTER_NODE_NOT_PAUSED cpu_to_le32(0xC0130014) +#define STATUS_CLUSTER_NO_SECURITY_CONTEXT cpu_to_le32(0xC0130015) +#define STATUS_CLUSTER_NETWORK_NOT_INTERNAL cpu_to_le32(0xC0130016) +#define STATUS_CLUSTER_POISONED cpu_to_le32(0xC0130017) +#define STATUS_ACPI_INVALID_OPCODE cpu_to_le32(0xC0140001) +#define STATUS_ACPI_STACK_OVERFLOW cpu_to_le32(0xC0140002) +#define STATUS_ACPI_ASSERT_FAILED cpu_to_le32(0xC0140003) +#define STATUS_ACPI_INVALID_INDEX cpu_to_le32(0xC0140004) +#define STATUS_ACPI_INVALID_ARGUMENT cpu_to_le32(0xC0140005) +#define STATUS_ACPI_FATAL cpu_to_le32(0xC0140006) +#define STATUS_ACPI_INVALID_SUPERNAME cpu_to_le32(0xC0140007) +#define STATUS_ACPI_INVALID_ARGTYPE cpu_to_le32(0xC0140008) +#define STATUS_ACPI_INVALID_OBJTYPE cpu_to_le32(0xC0140009) +#define STATUS_ACPI_INVALID_TARGETTYPE cpu_to_le32(0xC014000A) +#define STATUS_ACPI_INCORRECT_ARGUMENT_COUNT cpu_to_le32(0xC014000B) +#define STATUS_ACPI_ADDRESS_NOT_MAPPED cpu_to_le32(0xC014000C) +#define STATUS_ACPI_INVALID_EVENTTYPE cpu_to_le32(0xC014000D) +#define STATUS_ACPI_HANDLER_COLLISION cpu_to_le32(0xC014000E) +#define STATUS_ACPI_INVALID_DATA cpu_to_le32(0xC014000F) +#define STATUS_ACPI_INVALID_REGION cpu_to_le32(0xC0140010) +#define STATUS_ACPI_INVALID_ACCESS_SIZE cpu_to_le32(0xC0140011) +#define STATUS_ACPI_ACQUIRE_GLOBAL_LOCK cpu_to_le32(0xC0140012) +#define STATUS_ACPI_ALREADY_INITIALIZED cpu_to_le32(0xC0140013) +#define STATUS_ACPI_NOT_INITIALIZED cpu_to_le32(0xC0140014) +#define STATUS_ACPI_INVALID_MUTEX_LEVEL cpu_to_le32(0xC0140015) +#define STATUS_ACPI_MUTEX_NOT_OWNED cpu_to_le32(0xC0140016) +#define STATUS_ACPI_MUTEX_NOT_OWNER cpu_to_le32(0xC0140017) +#define STATUS_ACPI_RS_ACCESS cpu_to_le32(0xC0140018) +#define STATUS_ACPI_INVALID_TABLE cpu_to_le32(0xC0140019) +#define STATUS_ACPI_REG_HANDLER_FAILED cpu_to_le32(0xC0140020) +#define STATUS_ACPI_POWER_REQUEST_FAILED cpu_to_le32(0xC0140021) +#define STATUS_SXS_SECTION_NOT_FOUND cpu_to_le32(0xC0150001) +#define STATUS_SXS_CANT_GEN_ACTCTX cpu_to_le32(0xC0150002) +#define STATUS_SXS_INVALID_ACTCTXDATA_FORMAT cpu_to_le32(0xC0150003) +#define STATUS_SXS_ASSEMBLY_NOT_FOUND cpu_to_le32(0xC0150004) +#define STATUS_SXS_MANIFEST_FORMAT_ERROR cpu_to_le32(0xC0150005) +#define STATUS_SXS_MANIFEST_PARSE_ERROR cpu_to_le32(0xC0150006) +#define STATUS_SXS_ACTIVATION_CONTEXT_DISABLED cpu_to_le32(0xC0150007) +#define STATUS_SXS_KEY_NOT_FOUND cpu_to_le32(0xC0150008) +#define STATUS_SXS_VERSION_CONFLICT cpu_to_le32(0xC0150009) +#define STATUS_SXS_WRONG_SECTION_TYPE cpu_to_le32(0xC015000A) +#define STATUS_SXS_THREAD_QUERIES_DISABLED cpu_to_le32(0xC015000B) +#define STATUS_SXS_ASSEMBLY_MISSING cpu_to_le32(0xC015000C) +#define STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET cpu_to_le32(0xC015000E) +#define STATUS_SXS_EARLY_DEACTIVATION cpu_to_le32(0xC015000F) +#define STATUS_SXS_INVALID_DEACTIVATION cpu_to_le32(0xC0150010) +#define STATUS_SXS_MULTIPLE_DEACTIVATION cpu_to_le32(0xC0150011) +#define STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY \ + cpu_to_le32(0xC0150012) +#define STATUS_SXS_PROCESS_TERMINATION_REQUESTED cpu_to_le32(0xC0150013) +#define STATUS_SXS_CORRUPT_ACTIVATION_STACK cpu_to_le32(0xC0150014) +#define STATUS_SXS_CORRUPTION cpu_to_le32(0xC0150015) +#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE cpu_to_le32(0xC0150016) +#define STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME cpu_to_le32(0xC0150017) +#define STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE cpu_to_le32(0xC0150018) +#define STATUS_SXS_IDENTITY_PARSE_ERROR cpu_to_le32(0xC0150019) +#define STATUS_SXS_COMPONENT_STORE_CORRUPT cpu_to_le32(0xC015001A) +#define STATUS_SXS_FILE_HASH_MISMATCH cpu_to_le32(0xC015001B) +#define STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT \ + cpu_to_le32(0xC015001C) +#define STATUS_SXS_IDENTITIES_DIFFERENT cpu_to_le32(0xC015001D) +#define STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT cpu_to_le32(0xC015001E) +#define STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY cpu_to_le32(0xC015001F) +#define STATUS_ADVANCED_INSTALLER_FAILED cpu_to_le32(0xC0150020) +#define STATUS_XML_ENCODING_MISMATCH cpu_to_le32(0xC0150021) +#define STATUS_SXS_MANIFEST_TOO_BIG cpu_to_le32(0xC0150022) +#define STATUS_SXS_SETTING_NOT_REGISTERED cpu_to_le32(0xC0150023) +#define STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE cpu_to_le32(0xC0150024) +#define STATUS_SMI_PRIMITIVE_INSTALLER_FAILED cpu_to_le32(0xC0150025) +#define STATUS_GENERIC_COMMAND_FAILED cpu_to_le32(0xC0150026) +#define STATUS_SXS_FILE_HASH_MISSING cpu_to_le32(0xC0150027) +#define STATUS_TRANSACTIONAL_CONFLICT cpu_to_le32(0xC0190001) +#define STATUS_INVALID_TRANSACTION cpu_to_le32(0xC0190002) +#define STATUS_TRANSACTION_NOT_ACTIVE cpu_to_le32(0xC0190003) +#define STATUS_TM_INITIALIZATION_FAILED cpu_to_le32(0xC0190004) +#define STATUS_RM_NOT_ACTIVE cpu_to_le32(0xC0190005) +#define STATUS_RM_METADATA_CORRUPT cpu_to_le32(0xC0190006) +#define STATUS_TRANSACTION_NOT_JOINED cpu_to_le32(0xC0190007) +#define STATUS_DIRECTORY_NOT_RM cpu_to_le32(0xC0190008) +#define STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE cpu_to_le32(0xC019000A) +#define STATUS_LOG_RESIZE_INVALID_SIZE cpu_to_le32(0xC019000B) +#define STATUS_REMOTE_FILE_VERSION_MISMATCH cpu_to_le32(0xC019000C) +#define STATUS_CRM_PROTOCOL_ALREADY_EXISTS cpu_to_le32(0xC019000F) +#define STATUS_TRANSACTION_PROPAGATION_FAILED cpu_to_le32(0xC0190010) +#define STATUS_CRM_PROTOCOL_NOT_FOUND cpu_to_le32(0xC0190011) +#define STATUS_TRANSACTION_SUPERIOR_EXISTS cpu_to_le32(0xC0190012) +#define STATUS_TRANSACTION_REQUEST_NOT_VALID cpu_to_le32(0xC0190013) +#define STATUS_TRANSACTION_NOT_REQUESTED cpu_to_le32(0xC0190014) +#define STATUS_TRANSACTION_ALREADY_ABORTED cpu_to_le32(0xC0190015) +#define STATUS_TRANSACTION_ALREADY_COMMITTED cpu_to_le32(0xC0190016) +#define STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER cpu_to_le32(0xC0190017) +#define STATUS_CURRENT_TRANSACTION_NOT_VALID cpu_to_le32(0xC0190018) +#define STATUS_LOG_GROWTH_FAILED cpu_to_le32(0xC0190019) +#define STATUS_OBJECT_NO_LONGER_EXISTS cpu_to_le32(0xC0190021) +#define STATUS_STREAM_MINIVERSION_NOT_FOUND cpu_to_le32(0xC0190022) +#define STATUS_STREAM_MINIVERSION_NOT_VALID cpu_to_le32(0xC0190023) +#define STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION \ + cpu_to_le32(0xC0190024) +#define STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT cpu_to_le32(0xC0190025) +#define STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS cpu_to_le32(0xC0190026) +#define STATUS_HANDLE_NO_LONGER_VALID cpu_to_le32(0xC0190028) +#define STATUS_LOG_CORRUPTION_DETECTED cpu_to_le32(0xC0190030) +#define STATUS_RM_DISCONNECTED cpu_to_le32(0xC0190032) +#define STATUS_ENLISTMENT_NOT_SUPERIOR cpu_to_le32(0xC0190033) +#define STATUS_FILE_IDENTITY_NOT_PERSISTENT cpu_to_le32(0xC0190036) +#define STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY cpu_to_le32(0xC0190037) +#define STATUS_CANT_CROSS_RM_BOUNDARY cpu_to_le32(0xC0190038) +#define STATUS_TXF_DIR_NOT_EMPTY cpu_to_le32(0xC0190039) +#define STATUS_INDOUBT_TRANSACTIONS_EXIST cpu_to_le32(0xC019003A) +#define STATUS_TM_VOLATILE cpu_to_le32(0xC019003B) +#define STATUS_ROLLBACK_TIMER_EXPIRED cpu_to_le32(0xC019003C) +#define STATUS_TXF_ATTRIBUTE_CORRUPT cpu_to_le32(0xC019003D) +#define STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC019003E) +#define STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED cpu_to_le32(0xC019003F) +#define STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE cpu_to_le32(0xC0190040) +#define STATUS_TRANSACTION_REQUIRED_PROMOTION cpu_to_le32(0xC0190043) +#define STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION cpu_to_le32(0xC0190044) +#define STATUS_TRANSACTIONS_NOT_FROZEN cpu_to_le32(0xC0190045) +#define STATUS_TRANSACTION_FREEZE_IN_PROGRESS cpu_to_le32(0xC0190046) +#define STATUS_NOT_SNAPSHOT_VOLUME cpu_to_le32(0xC0190047) +#define STATUS_NO_SAVEPOINT_WITH_OPEN_FILES cpu_to_le32(0xC0190048) +#define STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC0190049) +#define STATUS_TM_IDENTITY_MISMATCH cpu_to_le32(0xC019004A) +#define STATUS_FLOATED_SECTION cpu_to_le32(0xC019004B) +#define STATUS_CANNOT_ACCEPT_TRANSACTED_WORK cpu_to_le32(0xC019004C) +#define STATUS_CANNOT_ABORT_TRANSACTIONS cpu_to_le32(0xC019004D) +#define STATUS_TRANSACTION_NOT_FOUND cpu_to_le32(0xC019004E) +#define STATUS_RESOURCEMANAGER_NOT_FOUND cpu_to_le32(0xC019004F) +#define STATUS_ENLISTMENT_NOT_FOUND cpu_to_le32(0xC0190050) +#define STATUS_TRANSACTIONMANAGER_NOT_FOUND cpu_to_le32(0xC0190051) +#define STATUS_TRANSACTIONMANAGER_NOT_ONLINE cpu_to_le32(0xC0190052) +#define STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION \ + cpu_to_le32(0xC0190053) +#define STATUS_TRANSACTION_NOT_ROOT cpu_to_le32(0xC0190054) +#define STATUS_TRANSACTION_OBJECT_EXPIRED cpu_to_le32(0xC0190055) +#define STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION cpu_to_le32(0xC0190056) +#define STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED cpu_to_le32(0xC0190057) +#define STATUS_TRANSACTION_RECORD_TOO_LONG cpu_to_le32(0xC0190058) +#define STATUS_NO_LINK_TRACKING_IN_TRANSACTION cpu_to_le32(0xC0190059) +#define STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION cpu_to_le32(0xC019005A) +#define STATUS_TRANSACTION_INTEGRITY_VIOLATED cpu_to_le32(0xC019005B) +#define STATUS_LOG_SECTOR_INVALID cpu_to_le32(0xC01A0001) +#define STATUS_LOG_SECTOR_PARITY_INVALID cpu_to_le32(0xC01A0002) +#define STATUS_LOG_SECTOR_REMAPPED cpu_to_le32(0xC01A0003) +#define STATUS_LOG_BLOCK_INCOMPLETE cpu_to_le32(0xC01A0004) +#define STATUS_LOG_INVALID_RANGE cpu_to_le32(0xC01A0005) +#define STATUS_LOG_BLOCKS_EXHAUSTED cpu_to_le32(0xC01A0006) +#define STATUS_LOG_READ_CONTEXT_INVALID cpu_to_le32(0xC01A0007) +#define STATUS_LOG_RESTART_INVALID cpu_to_le32(0xC01A0008) +#define STATUS_LOG_BLOCK_VERSION cpu_to_le32(0xC01A0009) +#define STATUS_LOG_BLOCK_INVALID cpu_to_le32(0xC01A000A) +#define STATUS_LOG_READ_MODE_INVALID cpu_to_le32(0xC01A000B) +#define STATUS_LOG_METADATA_CORRUPT cpu_to_le32(0xC01A000D) +#define STATUS_LOG_METADATA_INVALID cpu_to_le32(0xC01A000E) +#define STATUS_LOG_METADATA_INCONSISTENT cpu_to_le32(0xC01A000F) +#define STATUS_LOG_RESERVATION_INVALID cpu_to_le32(0xC01A0010) +#define STATUS_LOG_CANT_DELETE cpu_to_le32(0xC01A0011) +#define STATUS_LOG_CONTAINER_LIMIT_EXCEEDED cpu_to_le32(0xC01A0012) +#define STATUS_LOG_START_OF_LOG cpu_to_le32(0xC01A0013) +#define STATUS_LOG_POLICY_ALREADY_INSTALLED cpu_to_le32(0xC01A0014) +#define STATUS_LOG_POLICY_NOT_INSTALLED cpu_to_le32(0xC01A0015) +#define STATUS_LOG_POLICY_INVALID cpu_to_le32(0xC01A0016) +#define STATUS_LOG_POLICY_CONFLICT cpu_to_le32(0xC01A0017) +#define STATUS_LOG_PINNED_ARCHIVE_TAIL cpu_to_le32(0xC01A0018) +#define STATUS_LOG_RECORD_NONEXISTENT cpu_to_le32(0xC01A0019) +#define STATUS_LOG_RECORDS_RESERVED_INVALID cpu_to_le32(0xC01A001A) +#define STATUS_LOG_SPACE_RESERVED_INVALID cpu_to_le32(0xC01A001B) +#define STATUS_LOG_TAIL_INVALID cpu_to_le32(0xC01A001C) +#define STATUS_LOG_FULL cpu_to_le32(0xC01A001D) +#define STATUS_LOG_MULTIPLEXED cpu_to_le32(0xC01A001E) +#define STATUS_LOG_DEDICATED cpu_to_le32(0xC01A001F) +#define STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS cpu_to_le32(0xC01A0020) +#define STATUS_LOG_ARCHIVE_IN_PROGRESS cpu_to_le32(0xC01A0021) +#define STATUS_LOG_EPHEMERAL cpu_to_le32(0xC01A0022) +#define STATUS_LOG_NOT_ENOUGH_CONTAINERS cpu_to_le32(0xC01A0023) +#define STATUS_LOG_CLIENT_ALREADY_REGISTERED cpu_to_le32(0xC01A0024) +#define STATUS_LOG_CLIENT_NOT_REGISTERED cpu_to_le32(0xC01A0025) +#define STATUS_LOG_FULL_HANDLER_IN_PROGRESS cpu_to_le32(0xC01A0026) +#define STATUS_LOG_CONTAINER_READ_FAILED cpu_to_le32(0xC01A0027) +#define STATUS_LOG_CONTAINER_WRITE_FAILED cpu_to_le32(0xC01A0028) +#define STATUS_LOG_CONTAINER_OPEN_FAILED cpu_to_le32(0xC01A0029) +#define STATUS_LOG_CONTAINER_STATE_INVALID cpu_to_le32(0xC01A002A) +#define STATUS_LOG_STATE_INVALID cpu_to_le32(0xC01A002B) +#define STATUS_LOG_PINNED cpu_to_le32(0xC01A002C) +#define STATUS_LOG_METADATA_FLUSH_FAILED cpu_to_le32(0xC01A002D) +#define STATUS_LOG_INCONSISTENT_SECURITY cpu_to_le32(0xC01A002E) +#define STATUS_LOG_APPENDED_FLUSH_FAILED cpu_to_le32(0xC01A002F) +#define STATUS_LOG_PINNED_RESERVATION cpu_to_le32(0xC01A0030) +#define STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD cpu_to_le32(0xC01B00EA) +#define STATUS_FLT_NO_HANDLER_DEFINED cpu_to_le32(0xC01C0001) +#define STATUS_FLT_CONTEXT_ALREADY_DEFINED cpu_to_le32(0xC01C0002) +#define STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST cpu_to_le32(0xC01C0003) +#define STATUS_FLT_DISALLOW_FAST_IO cpu_to_le32(0xC01C0004) +#define STATUS_FLT_INVALID_NAME_REQUEST cpu_to_le32(0xC01C0005) +#define STATUS_FLT_NOT_SAFE_TO_POST_OPERATION cpu_to_le32(0xC01C0006) +#define STATUS_FLT_NOT_INITIALIZED cpu_to_le32(0xC01C0007) +#define STATUS_FLT_FILTER_NOT_READY cpu_to_le32(0xC01C0008) +#define STATUS_FLT_POST_OPERATION_CLEANUP cpu_to_le32(0xC01C0009) +#define STATUS_FLT_INTERNAL_ERROR cpu_to_le32(0xC01C000A) +#define STATUS_FLT_DELETING_OBJECT cpu_to_le32(0xC01C000B) +#define STATUS_FLT_MUST_BE_NONPAGED_POOL cpu_to_le32(0xC01C000C) +#define STATUS_FLT_DUPLICATE_ENTRY cpu_to_le32(0xC01C000D) +#define STATUS_FLT_CBDQ_DISABLED cpu_to_le32(0xC01C000E) +#define STATUS_FLT_DO_NOT_ATTACH cpu_to_le32(0xC01C000F) +#define STATUS_FLT_DO_NOT_DETACH cpu_to_le32(0xC01C0010) +#define STATUS_FLT_INSTANCE_ALTITUDE_COLLISION cpu_to_le32(0xC01C0011) +#define STATUS_FLT_INSTANCE_NAME_COLLISION cpu_to_le32(0xC01C0012) +#define STATUS_FLT_FILTER_NOT_FOUND cpu_to_le32(0xC01C0013) +#define STATUS_FLT_VOLUME_NOT_FOUND cpu_to_le32(0xC01C0014) +#define STATUS_FLT_INSTANCE_NOT_FOUND cpu_to_le32(0xC01C0015) +#define STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND cpu_to_le32(0xC01C0016) +#define STATUS_FLT_INVALID_CONTEXT_REGISTRATION cpu_to_le32(0xC01C0017) +#define STATUS_FLT_NAME_CACHE_MISS cpu_to_le32(0xC01C0018) +#define STATUS_FLT_NO_DEVICE_OBJECT cpu_to_le32(0xC01C0019) +#define STATUS_FLT_VOLUME_ALREADY_MOUNTED cpu_to_le32(0xC01C001A) +#define STATUS_FLT_ALREADY_ENLISTED cpu_to_le32(0xC01C001B) +#define STATUS_FLT_CONTEXT_ALREADY_LINKED cpu_to_le32(0xC01C001C) +#define STATUS_FLT_NO_WAITER_FOR_REPLY cpu_to_le32(0xC01C0020) +#define STATUS_MONITOR_NO_DESCRIPTOR cpu_to_le32(0xC01D0001) +#define STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT cpu_to_le32(0xC01D0002) +#define STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM cpu_to_le32(0xC01D0003) +#define STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK cpu_to_le32(0xC01D0004) +#define STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED cpu_to_le32(0xC01D0005) +#define STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK \ + cpu_to_le32(0xC01D0006) +#define STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK \ + cpu_to_le32(0xC01D0007) +#define STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA cpu_to_le32(0xC01D0008) +#define STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK cpu_to_le32(0xC01D0009) +#define STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER cpu_to_le32(0xC01E0000) +#define STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER cpu_to_le32(0xC01E0001) +#define STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER cpu_to_le32(0xC01E0002) +#define STATUS_GRAPHICS_ADAPTER_WAS_RESET cpu_to_le32(0xC01E0003) +#define STATUS_GRAPHICS_INVALID_DRIVER_MODEL cpu_to_le32(0xC01E0004) +#define STATUS_GRAPHICS_PRESENT_MODE_CHANGED cpu_to_le32(0xC01E0005) +#define STATUS_GRAPHICS_PRESENT_OCCLUDED cpu_to_le32(0xC01E0006) +#define STATUS_GRAPHICS_PRESENT_DENIED cpu_to_le32(0xC01E0007) +#define STATUS_GRAPHICS_CANNOTCOLORCONVERT cpu_to_le32(0xC01E0008) +#define STATUS_GRAPHICS_NO_VIDEO_MEMORY cpu_to_le32(0xC01E0100) +#define STATUS_GRAPHICS_CANT_LOCK_MEMORY cpu_to_le32(0xC01E0101) +#define STATUS_GRAPHICS_ALLOCATION_BUSY cpu_to_le32(0xC01E0102) +#define STATUS_GRAPHICS_TOO_MANY_REFERENCES cpu_to_le32(0xC01E0103) +#define STATUS_GRAPHICS_TRY_AGAIN_LATER cpu_to_le32(0xC01E0104) +#define STATUS_GRAPHICS_TRY_AGAIN_NOW cpu_to_le32(0xC01E0105) +#define STATUS_GRAPHICS_ALLOCATION_INVALID cpu_to_le32(0xC01E0106) +#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE cpu_to_le32(0xC01E0107) +#define STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED cpu_to_le32(0xC01E0108) +#define STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION cpu_to_le32(0xC01E0109) +#define STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE cpu_to_le32(0xC01E0110) +#define STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION cpu_to_le32(0xC01E0111) +#define STATUS_GRAPHICS_ALLOCATION_CLOSED cpu_to_le32(0xC01E0112) +#define STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE cpu_to_le32(0xC01E0113) +#define STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE cpu_to_le32(0xC01E0114) +#define STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE cpu_to_le32(0xC01E0115) +#define STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST cpu_to_le32(0xC01E0116) +#define STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE cpu_to_le32(0xC01E0200) +#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY cpu_to_le32(0xC01E0300) +#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED cpu_to_le32(0xC01E0301) +#define STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED \ + cpu_to_le32(0xC01E0302) +#define STATUS_GRAPHICS_INVALID_VIDPN cpu_to_le32(0xC01E0303) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE cpu_to_le32(0xC01E0304) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET cpu_to_le32(0xC01E0305) +#define STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED cpu_to_le32(0xC01E0306) +#define STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET cpu_to_le32(0xC01E0308) +#define STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET cpu_to_le32(0xC01E0309) +#define STATUS_GRAPHICS_INVALID_FREQUENCY cpu_to_le32(0xC01E030A) +#define STATUS_GRAPHICS_INVALID_ACTIVE_REGION cpu_to_le32(0xC01E030B) +#define STATUS_GRAPHICS_INVALID_TOTAL_REGION cpu_to_le32(0xC01E030C) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE \ + cpu_to_le32(0xC01E0310) +#define STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE \ + cpu_to_le32(0xC01E0311) +#define STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET cpu_to_le32(0xC01E0312) +#define STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY cpu_to_le32(0xC01E0313) +#define STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET cpu_to_le32(0xC01E0314) +#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET cpu_to_le32(0xC01E0315) +#define STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET cpu_to_le32(0xC01E0316) +#define STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET cpu_to_le32(0xC01E0317) +#define STATUS_GRAPHICS_TARGET_ALREADY_IN_SET cpu_to_le32(0xC01E0318) +#define STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH cpu_to_le32(0xC01E0319) +#define STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY cpu_to_le32(0xC01E031A) +#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET \ + cpu_to_le32(0xC01E031B) +#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE cpu_to_le32(0xC01E031C) +#define STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET cpu_to_le32(0xC01E031D) +#define STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET cpu_to_le32(0xC01E031F) +#define STATUS_GRAPHICS_STALE_MODESET cpu_to_le32(0xC01E0320) +#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET cpu_to_le32(0xC01E0321) +#define STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE cpu_to_le32(0xC01E0322) +#define STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN cpu_to_le32(0xC01E0323) +#define STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0324) +#define STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION \ + cpu_to_le32(0xC01E0325) +#define STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES \ + cpu_to_le32(0xC01E0326) +#define STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0327) +#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE \ + cpu_to_le32(0xC01E0328) +#define STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET \ + cpu_to_le32(0xC01E0329) +#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET cpu_to_le32(0xC01E032A) +#define STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR cpu_to_le32(0xC01E032B) +#define STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET cpu_to_le32(0xC01E032C) +#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET cpu_to_le32(0xC01E032D) +#define STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE \ + cpu_to_le32(0xC01E032E) +#define STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE cpu_to_le32(0xC01E032F) +#define STATUS_GRAPHICS_RESOURCES_NOT_RELATED cpu_to_le32(0xC01E0330) +#define STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0331) +#define STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE cpu_to_le32(0xC01E0332) +#define STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET cpu_to_le32(0xC01E0333) +#define STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER \ + cpu_to_le32(0xC01E0334) +#define STATUS_GRAPHICS_NO_VIDPNMGR cpu_to_le32(0xC01E0335) +#define STATUS_GRAPHICS_NO_ACTIVE_VIDPN cpu_to_le32(0xC01E0336) +#define STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY cpu_to_le32(0xC01E0337) +#define STATUS_GRAPHICS_MONITOR_NOT_CONNECTED cpu_to_le32(0xC01E0338) +#define STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0339) +#define STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE cpu_to_le32(0xC01E033A) +#define STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE cpu_to_le32(0xC01E033B) +#define STATUS_GRAPHICS_INVALID_STRIDE cpu_to_le32(0xC01E033C) +#define STATUS_GRAPHICS_INVALID_PIXELFORMAT cpu_to_le32(0xC01E033D) +#define STATUS_GRAPHICS_INVALID_COLORBASIS cpu_to_le32(0xC01E033E) +#define STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE cpu_to_le32(0xC01E033F) +#define STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY cpu_to_le32(0xC01E0340) +#define STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT \ + cpu_to_le32(0xC01E0341) +#define STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE cpu_to_le32(0xC01E0342) +#define STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN cpu_to_le32(0xC01E0343) +#define STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL cpu_to_le32(0xC01E0344) +#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION \ + cpu_to_le32(0xC01E0345) +#define STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED \ + cpu_to_le32(0xC01E0346) +#define STATUS_GRAPHICS_INVALID_GAMMA_RAMP cpu_to_le32(0xC01E0347) +#define STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED cpu_to_le32(0xC01E0348) +#define STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED cpu_to_le32(0xC01E0349) +#define STATUS_GRAPHICS_MODE_NOT_IN_MODESET cpu_to_le32(0xC01E034A) +#define STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON \ + cpu_to_le32(0xC01E034D) +#define STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE cpu_to_le32(0xC01E034E) +#define STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE cpu_to_le32(0xC01E034F) +#define STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS \ + cpu_to_le32(0xC01E0350) +#define STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING cpu_to_le32(0xC01E0352) +#define STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED cpu_to_le32(0xC01E0353) +#define STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS cpu_to_le32(0xC01E0354) +#define STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT cpu_to_le32(0xC01E0355) +#define STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM cpu_to_le32(0xC01E0356) +#define STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN \ + cpu_to_le32(0xC01E0357) +#define STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT \ + cpu_to_le32(0xC01E0358) +#define STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED cpu_to_le32(0xC01E0359) +#define STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION \ + cpu_to_le32(0xC01E035A) +#define STATUS_GRAPHICS_INVALID_CLIENT_TYPE cpu_to_le32(0xC01E035B) +#define STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET cpu_to_le32(0xC01E035C) +#define STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED \ + cpu_to_le32(0xC01E0400) +#define STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED cpu_to_le32(0xC01E0401) +#define STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER cpu_to_le32(0xC01E0430) +#define STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED cpu_to_le32(0xC01E0431) +#define STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED cpu_to_le32(0xC01E0432) +#define STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY cpu_to_le32(0xC01E0433) +#define STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED cpu_to_le32(0xC01E0434) +#define STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON cpu_to_le32(0xC01E0435) +#define STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE cpu_to_le32(0xC01E0436) +#define STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER cpu_to_le32(0xC01E0438) +#define STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED cpu_to_le32(0xC01E043B) +#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS \ + cpu_to_le32(0xC01E051C) +#define STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST cpu_to_le32(0xC01E051D) +#define STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR cpu_to_le32(0xC01E051E) +#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS \ + cpu_to_le32(0xC01E051F) +#define STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED cpu_to_le32(0xC01E0520) +#define STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST \ + cpu_to_le32(0xC01E0521) +#define STATUS_GRAPHICS_OPM_NOT_SUPPORTED cpu_to_le32(0xC01E0500) +#define STATUS_GRAPHICS_COPP_NOT_SUPPORTED cpu_to_le32(0xC01E0501) +#define STATUS_GRAPHICS_UAB_NOT_SUPPORTED cpu_to_le32(0xC01E0502) +#define STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS cpu_to_le32(0xC01E0503) +#define STATUS_GRAPHICS_OPM_PARAMETER_ARRAY_TOO_SMALL cpu_to_le32(0xC01E0504) +#define STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST cpu_to_le32(0xC01E0505) +#define STATUS_GRAPHICS_PVP_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME \ + cpu_to_le32(0xC01E0506) +#define STATUS_GRAPHICS_PVP_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP \ + cpu_to_le32(0xC01E0507) +#define STATUS_GRAPHICS_PVP_MIRRORING_DEVICES_NOT_SUPPORTED \ + cpu_to_le32(0xC01E0508) +#define STATUS_GRAPHICS_OPM_INVALID_POINTER cpu_to_le32(0xC01E050A) +#define STATUS_GRAPHICS_OPM_INTERNAL_ERROR cpu_to_le32(0xC01E050B) +#define STATUS_GRAPHICS_OPM_INVALID_HANDLE cpu_to_le32(0xC01E050C) +#define STATUS_GRAPHICS_PVP_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE \ + cpu_to_le32(0xC01E050D) +#define STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH cpu_to_le32(0xC01E050E) +#define STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED cpu_to_le32(0xC01E050F) +#define STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED cpu_to_le32(0xC01E0510) +#define STATUS_GRAPHICS_PVP_HFS_FAILED cpu_to_le32(0xC01E0511) +#define STATUS_GRAPHICS_OPM_INVALID_SRM cpu_to_le32(0xC01E0512) +#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP cpu_to_le32(0xC01E0513) +#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP cpu_to_le32(0xC01E0514) +#define STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA \ + cpu_to_le32(0xC01E0515) +#define STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET cpu_to_le32(0xC01E0516) +#define STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH cpu_to_le32(0xC01E0517) +#define STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE \ + cpu_to_le32(0xC01E0518) +#define STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS \ + cpu_to_le32(0xC01E051A) +#define STATUS_GRAPHICS_OPM_SESSION_TYPE_CHANGE_IN_PROGRESS \ + cpu_to_le32(0xC01E051B) +#define STATUS_GRAPHICS_I2C_NOT_SUPPORTED cpu_to_le32(0xC01E0580) +#define STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST cpu_to_le32(0xC01E0581) +#define STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA cpu_to_le32(0xC01E0582) +#define STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA cpu_to_le32(0xC01E0583) +#define STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED cpu_to_le32(0xC01E0584) +#define STATUS_GRAPHICS_DDCCI_INVALID_DATA cpu_to_le32(0xC01E0585) +#define STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE \ + cpu_to_le32(0xC01E0586) +#define STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING \ + cpu_to_le32(0xC01E0587) +#define STATUS_GRAPHICS_MCA_INTERNAL_ERROR cpu_to_le32(0xC01E0588) +#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND cpu_to_le32(0xC01E0589) +#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH cpu_to_le32(0xC01E058A) +#define STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM cpu_to_le32(0xC01E058B) +#define STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE cpu_to_le32(0xC01E058C) +#define STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS cpu_to_le32(0xC01E058D) +#define STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED cpu_to_le32(0xC01E05E0) +#define STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME \ + cpu_to_le32(0xC01E05E1) +#define STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP \ + cpu_to_le32(0xC01E05E2) +#define STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED cpu_to_le32(0xC01E05E3) +#define STATUS_GRAPHICS_INVALID_POINTER cpu_to_le32(0xC01E05E4) +#define STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE \ + cpu_to_le32(0xC01E05E5) +#define STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL cpu_to_le32(0xC01E05E6) +#define STATUS_GRAPHICS_INTERNAL_ERROR cpu_to_le32(0xC01E05E7) +#define STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS cpu_to_le32(0xC01E05E8) +#define STATUS_FVE_LOCKED_VOLUME cpu_to_le32(0xC0210000) +#define STATUS_FVE_NOT_ENCRYPTED cpu_to_le32(0xC0210001) +#define STATUS_FVE_BAD_INFORMATION cpu_to_le32(0xC0210002) +#define STATUS_FVE_TOO_SMALL cpu_to_le32(0xC0210003) +#define STATUS_FVE_FAILED_WRONG_FS cpu_to_le32(0xC0210004) +#define STATUS_FVE_FAILED_BAD_FS cpu_to_le32(0xC0210005) +#define STATUS_FVE_FS_NOT_EXTENDED cpu_to_le32(0xC0210006) +#define STATUS_FVE_FS_MOUNTED cpu_to_le32(0xC0210007) +#define STATUS_FVE_NO_LICENSE cpu_to_le32(0xC0210008) +#define STATUS_FVE_ACTION_NOT_ALLOWED cpu_to_le32(0xC0210009) +#define STATUS_FVE_BAD_DATA cpu_to_le32(0xC021000A) +#define STATUS_FVE_VOLUME_NOT_BOUND cpu_to_le32(0xC021000B) +#define STATUS_FVE_NOT_DATA_VOLUME cpu_to_le32(0xC021000C) +#define STATUS_FVE_CONV_READ_ERROR cpu_to_le32(0xC021000D) +#define STATUS_FVE_CONV_WRITE_ERROR cpu_to_le32(0xC021000E) +#define STATUS_FVE_OVERLAPPED_UPDATE cpu_to_le32(0xC021000F) +#define STATUS_FVE_FAILED_SECTOR_SIZE cpu_to_le32(0xC0210010) +#define STATUS_FVE_FAILED_AUTHENTICATION cpu_to_le32(0xC0210011) +#define STATUS_FVE_NOT_OS_VOLUME cpu_to_le32(0xC0210012) +#define STATUS_FVE_KEYFILE_NOT_FOUND cpu_to_le32(0xC0210013) +#define STATUS_FVE_KEYFILE_INVALID cpu_to_le32(0xC0210014) +#define STATUS_FVE_KEYFILE_NO_VMK cpu_to_le32(0xC0210015) +#define STATUS_FVE_TPM_DISABLED cpu_to_le32(0xC0210016) +#define STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO cpu_to_le32(0xC0210017) +#define STATUS_FVE_TPM_INVALID_PCR cpu_to_le32(0xC0210018) +#define STATUS_FVE_TPM_NO_VMK cpu_to_le32(0xC0210019) +#define STATUS_FVE_PIN_INVALID cpu_to_le32(0xC021001A) +#define STATUS_FVE_AUTH_INVALID_APPLICATION cpu_to_le32(0xC021001B) +#define STATUS_FVE_AUTH_INVALID_CONFIG cpu_to_le32(0xC021001C) +#define STATUS_FVE_DEBUGGER_ENABLED cpu_to_le32(0xC021001D) +#define STATUS_FVE_DRY_RUN_FAILED cpu_to_le32(0xC021001E) +#define STATUS_FVE_BAD_METADATA_POINTER cpu_to_le32(0xC021001F) +#define STATUS_FVE_OLD_METADATA_COPY cpu_to_le32(0xC0210020) +#define STATUS_FVE_REBOOT_REQUIRED cpu_to_le32(0xC0210021) +#define STATUS_FVE_RAW_ACCESS cpu_to_le32(0xC0210022) +#define STATUS_FVE_RAW_BLOCKED cpu_to_le32(0xC0210023) +#define STATUS_FWP_CALLOUT_NOT_FOUND cpu_to_le32(0xC0220001) +#define STATUS_FWP_CONDITION_NOT_FOUND cpu_to_le32(0xC0220002) +#define STATUS_FWP_FILTER_NOT_FOUND cpu_to_le32(0xC0220003) +#define STATUS_FWP_LAYER_NOT_FOUND cpu_to_le32(0xC0220004) +#define STATUS_FWP_PROVIDER_NOT_FOUND cpu_to_le32(0xC0220005) +#define STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND cpu_to_le32(0xC0220006) +#define STATUS_FWP_SUBLAYER_NOT_FOUND cpu_to_le32(0xC0220007) +#define STATUS_FWP_NOT_FOUND cpu_to_le32(0xC0220008) +#define STATUS_FWP_ALREADY_EXISTS cpu_to_le32(0xC0220009) +#define STATUS_FWP_IN_USE cpu_to_le32(0xC022000A) +#define STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS cpu_to_le32(0xC022000B) +#define STATUS_FWP_WRONG_SESSION cpu_to_le32(0xC022000C) +#define STATUS_FWP_NO_TXN_IN_PROGRESS cpu_to_le32(0xC022000D) +#define STATUS_FWP_TXN_IN_PROGRESS cpu_to_le32(0xC022000E) +#define STATUS_FWP_TXN_ABORTED cpu_to_le32(0xC022000F) +#define STATUS_FWP_SESSION_ABORTED cpu_to_le32(0xC0220010) +#define STATUS_FWP_INCOMPATIBLE_TXN cpu_to_le32(0xC0220011) +#define STATUS_FWP_TIMEOUT cpu_to_le32(0xC0220012) +#define STATUS_FWP_NET_EVENTS_DISABLED cpu_to_le32(0xC0220013) +#define STATUS_FWP_INCOMPATIBLE_LAYER cpu_to_le32(0xC0220014) +#define STATUS_FWP_KM_CLIENTS_ONLY cpu_to_le32(0xC0220015) +#define STATUS_FWP_LIFETIME_MISMATCH cpu_to_le32(0xC0220016) +#define STATUS_FWP_BUILTIN_OBJECT cpu_to_le32(0xC0220017) +#define STATUS_FWP_TOO_MANY_BOOTTIME_FILTERS cpu_to_le32(0xC0220018) +#define STATUS_FWP_TOO_MANY_CALLOUTS cpu_to_le32(0xC0220018) +#define STATUS_FWP_NOTIFICATION_DROPPED cpu_to_le32(0xC0220019) +#define STATUS_FWP_TRAFFIC_MISMATCH cpu_to_le32(0xC022001A) +#define STATUS_FWP_INCOMPATIBLE_SA_STATE cpu_to_le32(0xC022001B) +#define STATUS_FWP_NULL_POINTER cpu_to_le32(0xC022001C) +#define STATUS_FWP_INVALID_ENUMERATOR cpu_to_le32(0xC022001D) +#define STATUS_FWP_INVALID_FLAGS cpu_to_le32(0xC022001E) +#define STATUS_FWP_INVALID_NET_MASK cpu_to_le32(0xC022001F) +#define STATUS_FWP_INVALID_RANGE cpu_to_le32(0xC0220020) +#define STATUS_FWP_INVALID_INTERVAL cpu_to_le32(0xC0220021) +#define STATUS_FWP_ZERO_LENGTH_ARRAY cpu_to_le32(0xC0220022) +#define STATUS_FWP_NULL_DISPLAY_NAME cpu_to_le32(0xC0220023) +#define STATUS_FWP_INVALID_ACTION_TYPE cpu_to_le32(0xC0220024) +#define STATUS_FWP_INVALID_WEIGHT cpu_to_le32(0xC0220025) +#define STATUS_FWP_MATCH_TYPE_MISMATCH cpu_to_le32(0xC0220026) +#define STATUS_FWP_TYPE_MISMATCH cpu_to_le32(0xC0220027) +#define STATUS_FWP_OUT_OF_BOUNDS cpu_to_le32(0xC0220028) +#define STATUS_FWP_RESERVED cpu_to_le32(0xC0220029) +#define STATUS_FWP_DUPLICATE_CONDITION cpu_to_le32(0xC022002A) +#define STATUS_FWP_DUPLICATE_KEYMOD cpu_to_le32(0xC022002B) +#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER cpu_to_le32(0xC022002C) +#define STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER cpu_to_le32(0xC022002D) +#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER cpu_to_le32(0xC022002E) +#define STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT cpu_to_le32(0xC022002F) +#define STATUS_FWP_INCOMPATIBLE_AUTH_METHOD cpu_to_le32(0xC0220030) +#define STATUS_FWP_INCOMPATIBLE_DH_GROUP cpu_to_le32(0xC0220031) +#define STATUS_FWP_EM_NOT_SUPPORTED cpu_to_le32(0xC0220032) +#define STATUS_FWP_NEVER_MATCH cpu_to_le32(0xC0220033) +#define STATUS_FWP_PROVIDER_CONTEXT_MISMATCH cpu_to_le32(0xC0220034) +#define STATUS_FWP_INVALID_PARAMETER cpu_to_le32(0xC0220035) +#define STATUS_FWP_TOO_MANY_SUBLAYERS cpu_to_le32(0xC0220036) +#define STATUS_FWP_CALLOUT_NOTIFICATION_FAILED cpu_to_le32(0xC0220037) +#define STATUS_FWP_INCOMPATIBLE_AUTH_CONFIG cpu_to_le32(0xC0220038) +#define STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG cpu_to_le32(0xC0220039) +#define STATUS_FWP_TCPIP_NOT_READY cpu_to_le32(0xC0220100) +#define STATUS_FWP_INJECT_HANDLE_CLOSING cpu_to_le32(0xC0220101) +#define STATUS_FWP_INJECT_HANDLE_STALE cpu_to_le32(0xC0220102) +#define STATUS_FWP_CANNOT_PEND cpu_to_le32(0xC0220103) +#define STATUS_NDIS_CLOSING cpu_to_le32(0xC0230002) +#define STATUS_NDIS_BAD_VERSION cpu_to_le32(0xC0230004) +#define STATUS_NDIS_BAD_CHARACTERISTICS cpu_to_le32(0xC0230005) +#define STATUS_NDIS_ADAPTER_NOT_FOUND cpu_to_le32(0xC0230006) +#define STATUS_NDIS_OPEN_FAILED cpu_to_le32(0xC0230007) +#define STATUS_NDIS_DEVICE_FAILED cpu_to_le32(0xC0230008) +#define STATUS_NDIS_MULTICAST_FULL cpu_to_le32(0xC0230009) +#define STATUS_NDIS_MULTICAST_EXISTS cpu_to_le32(0xC023000A) +#define STATUS_NDIS_MULTICAST_NOT_FOUND cpu_to_le32(0xC023000B) +#define STATUS_NDIS_REQUEST_ABORTED cpu_to_le32(0xC023000C) +#define STATUS_NDIS_RESET_IN_PROGRESS cpu_to_le32(0xC023000D) +#define STATUS_NDIS_INVALID_PACKET cpu_to_le32(0xC023000F) +#define STATUS_NDIS_INVALID_DEVICE_REQUEST cpu_to_le32(0xC0230010) +#define STATUS_NDIS_ADAPTER_NOT_READY cpu_to_le32(0xC0230011) +#define STATUS_NDIS_INVALID_LENGTH cpu_to_le32(0xC0230014) +#define STATUS_NDIS_INVALID_DATA cpu_to_le32(0xC0230015) +#define STATUS_NDIS_BUFFER_TOO_SHORT cpu_to_le32(0xC0230016) +#define STATUS_NDIS_INVALID_OID cpu_to_le32(0xC0230017) +#define STATUS_NDIS_ADAPTER_REMOVED cpu_to_le32(0xC0230018) +#define STATUS_NDIS_UNSUPPORTED_MEDIA cpu_to_le32(0xC0230019) +#define STATUS_NDIS_GROUP_ADDRESS_IN_USE cpu_to_le32(0xC023001A) +#define STATUS_NDIS_FILE_NOT_FOUND cpu_to_le32(0xC023001B) +#define STATUS_NDIS_ERROR_READING_FILE cpu_to_le32(0xC023001C) +#define STATUS_NDIS_ALREADY_MAPPED cpu_to_le32(0xC023001D) +#define STATUS_NDIS_RESOURCE_CONFLICT cpu_to_le32(0xC023001E) +#define STATUS_NDIS_MEDIA_DISCONNECTED cpu_to_le32(0xC023001F) +#define STATUS_NDIS_INVALID_ADDRESS cpu_to_le32(0xC0230022) +#define STATUS_NDIS_PAUSED cpu_to_le32(0xC023002A) +#define STATUS_NDIS_INTERFACE_NOT_FOUND cpu_to_le32(0xC023002B) +#define STATUS_NDIS_UNSUPPORTED_REVISION cpu_to_le32(0xC023002C) +#define STATUS_NDIS_INVALID_PORT cpu_to_le32(0xC023002D) +#define STATUS_NDIS_INVALID_PORT_STATE cpu_to_le32(0xC023002E) +#define STATUS_NDIS_LOW_POWER_STATE cpu_to_le32(0xC023002F) +#define STATUS_NDIS_NOT_SUPPORTED cpu_to_le32(0xC02300BB) +#define STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED cpu_to_le32(0xC0232000) +#define STATUS_NDIS_DOT11_MEDIA_IN_USE cpu_to_le32(0xC0232001) +#define STATUS_NDIS_DOT11_POWER_STATE_INVALID cpu_to_le32(0xC0232002) +#define STATUS_IPSEC_BAD_SPI cpu_to_le32(0xC0360001) +#define STATUS_IPSEC_SA_LIFETIME_EXPIRED cpu_to_le32(0xC0360002) +#define STATUS_IPSEC_WRONG_SA cpu_to_le32(0xC0360003) +#define STATUS_IPSEC_REPLAY_CHECK_FAILED cpu_to_le32(0xC0360004) +#define STATUS_IPSEC_INVALID_PACKET cpu_to_le32(0xC0360005) +#define STATUS_IPSEC_INTEGRITY_CHECK_FAILED cpu_to_le32(0xC0360006) +#define STATUS_IPSEC_CLEAR_TEXT_DROP cpu_to_le32(0xC0360007) + +#define STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP cpu_to_le32(0xC05D0000) +#define STATUS_INVALID_LOCK_RANGE cpu_to_le32(0xC00001a1) diff --git a/fs/ksmbd/transport_ipc.c b/fs/ksmbd/transport_ipc.c new file mode 100644 index 0000000000000..d5a871da3a7d0 --- /dev/null +++ b/fs/ksmbd/transport_ipc.c @@ -0,0 +1,885 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vfs_cache.h" +#include "transport_ipc.h" +#include "server.h" +#include "smb_common.h" + +#include "mgmt/user_config.h" +#include "mgmt/share_config.h" +#include "mgmt/user_session.h" +#include "mgmt/tree_connect.h" +#include "mgmt/ksmbd_ida.h" +#include "connection.h" +#include "transport_tcp.h" +#include "transport_rdma.h" + +#define IPC_WAIT_TIMEOUT (2 * HZ) + +#define IPC_MSG_HASH_BITS 3 +static DEFINE_HASHTABLE(ipc_msg_table, IPC_MSG_HASH_BITS); +static DECLARE_RWSEM(ipc_msg_table_lock); +static DEFINE_MUTEX(startup_lock); + +static DEFINE_IDA(ipc_ida); + +static unsigned int ksmbd_tools_pid; + +static bool ksmbd_ipc_validate_version(struct genl_info *m) +{ + if (m->genlhdr->version != KSMBD_GENL_VERSION) { + pr_err("%s. ksmbd: %d, kernel module: %d. %s.\n", + "Daemon and kernel module version mismatch", + m->genlhdr->version, + KSMBD_GENL_VERSION, + "User-space ksmbd should terminate"); + return false; + } + return true; +} + +struct ksmbd_ipc_msg { + unsigned int type; + unsigned int sz; + unsigned char payload[]; +}; + +struct ipc_msg_table_entry { + unsigned int handle; + unsigned int type; + wait_queue_head_t wait; + struct hlist_node ipc_table_hlist; + + void *response; +}; + +static struct delayed_work ipc_timer_work; + +static int handle_startup_event(struct sk_buff *skb, struct genl_info *info); +static int handle_unsupported_event(struct sk_buff *skb, struct genl_info *info); +static int handle_generic_event(struct sk_buff *skb, struct genl_info *info); +static int ksmbd_ipc_heartbeat_request(void); + +static const struct nla_policy ksmbd_nl_policy[KSMBD_EVENT_MAX] = { + [KSMBD_EVENT_UNSPEC] = { + .len = 0, + }, + [KSMBD_EVENT_HEARTBEAT_REQUEST] = { + .len = sizeof(struct ksmbd_heartbeat), + }, + [KSMBD_EVENT_STARTING_UP] = { + .len = sizeof(struct ksmbd_startup_request), + }, + [KSMBD_EVENT_SHUTTING_DOWN] = { + .len = sizeof(struct ksmbd_shutdown_request), + }, + [KSMBD_EVENT_LOGIN_REQUEST] = { + .len = sizeof(struct ksmbd_login_request), + }, + [KSMBD_EVENT_LOGIN_RESPONSE] = { + .len = sizeof(struct ksmbd_login_response), + }, + [KSMBD_EVENT_SHARE_CONFIG_REQUEST] = { + .len = sizeof(struct ksmbd_share_config_request), + }, + [KSMBD_EVENT_SHARE_CONFIG_RESPONSE] = { + .len = sizeof(struct ksmbd_share_config_response), + }, + [KSMBD_EVENT_TREE_CONNECT_REQUEST] = { + .len = sizeof(struct ksmbd_tree_connect_request), + }, + [KSMBD_EVENT_TREE_CONNECT_RESPONSE] = { + .len = sizeof(struct ksmbd_tree_connect_response), + }, + [KSMBD_EVENT_TREE_DISCONNECT_REQUEST] = { + .len = sizeof(struct ksmbd_tree_disconnect_request), + }, + [KSMBD_EVENT_LOGOUT_REQUEST] = { + .len = sizeof(struct ksmbd_logout_request), + }, + [KSMBD_EVENT_RPC_REQUEST] = { + }, + [KSMBD_EVENT_RPC_RESPONSE] = { + }, + [KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST] = { + }, + [KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE] = { + }, +}; + +static struct genl_ops ksmbd_genl_ops[] = { + { + .cmd = KSMBD_EVENT_UNSPEC, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_HEARTBEAT_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_STARTING_UP, + .doit = handle_startup_event, + }, + { + .cmd = KSMBD_EVENT_SHUTTING_DOWN, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_LOGIN_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_LOGIN_RESPONSE, + .doit = handle_generic_event, + }, + { + .cmd = KSMBD_EVENT_SHARE_CONFIG_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_SHARE_CONFIG_RESPONSE, + .doit = handle_generic_event, + }, + { + .cmd = KSMBD_EVENT_TREE_CONNECT_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_TREE_CONNECT_RESPONSE, + .doit = handle_generic_event, + }, + { + .cmd = KSMBD_EVENT_TREE_DISCONNECT_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_LOGOUT_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_RPC_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_RPC_RESPONSE, + .doit = handle_generic_event, + }, + { + .cmd = KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST, + .doit = handle_unsupported_event, + }, + { + .cmd = KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE, + .doit = handle_generic_event, + }, +}; + +static struct genl_family ksmbd_genl_family = { + .name = KSMBD_GENL_NAME, + .version = KSMBD_GENL_VERSION, + .hdrsize = 0, + .maxattr = KSMBD_EVENT_MAX, + .netnsok = true, + .module = THIS_MODULE, + .ops = ksmbd_genl_ops, + .n_ops = ARRAY_SIZE(ksmbd_genl_ops), +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) + .resv_start_op = KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE + 1, +#endif +}; + +static void ksmbd_nl_init_fixup(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ksmbd_genl_ops); i++) + ksmbd_genl_ops[i].validate = GENL_DONT_VALIDATE_STRICT | + GENL_DONT_VALIDATE_DUMP; + + ksmbd_genl_family.policy = ksmbd_nl_policy; +} + +static int rpc_context_flags(struct ksmbd_session *sess) +{ + if (user_guest(sess->user)) + return KSMBD_RPC_RESTRICTED_CONTEXT; + return 0; +} + +static void ipc_update_last_active(void) +{ + if (server_conf.ipc_timeout) + server_conf.ipc_last_active = jiffies; +} + +static struct ksmbd_ipc_msg *ipc_msg_alloc(size_t sz) +{ + struct ksmbd_ipc_msg *msg; + size_t msg_sz = sz + sizeof(struct ksmbd_ipc_msg); + + msg = kvmalloc(msg_sz, GFP_KERNEL | __GFP_ZERO); + if (msg) + msg->sz = sz; + return msg; +} + +static void ipc_msg_free(struct ksmbd_ipc_msg *msg) +{ + kvfree(msg); +} + +static void ipc_msg_handle_free(int handle) +{ + if (handle >= 0) + ksmbd_release_id(&ipc_ida, handle); +} + +static int handle_response(int type, void *payload, size_t sz) +{ + unsigned int handle = *(unsigned int *)payload; + struct ipc_msg_table_entry *entry; + int ret = 0; + + ipc_update_last_active(); + down_read(&ipc_msg_table_lock); + hash_for_each_possible(ipc_msg_table, entry, ipc_table_hlist, handle) { + if (handle != entry->handle) + continue; + + entry->response = NULL; + /* + * Response message type value should be equal to + * request message type + 1. + */ + if (entry->type + 1 != type) { + pr_err("Waiting for IPC type %d, got %d. Ignore.\n", + entry->type + 1, type); + } + + entry->response = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO); + if (!entry->response) { + ret = -ENOMEM; + break; + } + + memcpy(entry->response, payload, sz); + wake_up_interruptible(&entry->wait); + ret = 0; + break; + } + up_read(&ipc_msg_table_lock); + + return ret; +} + +static int ipc_server_config_on_startup(struct ksmbd_startup_request *req) +{ + int ret; + + ksmbd_set_fd_limit(req->file_max); + server_conf.flags = req->flags; + server_conf.signing = req->signing; + server_conf.tcp_port = req->tcp_port; + server_conf.ipc_timeout = req->ipc_timeout * HZ; + server_conf.deadtime = req->deadtime * SMB_ECHO_INTERVAL; + server_conf.share_fake_fscaps = req->share_fake_fscaps; + ksmbd_init_domain(req->sub_auth); + + if (req->smb2_max_read) + init_smb2_max_read_size(req->smb2_max_read); + if (req->smb2_max_write) + init_smb2_max_write_size(req->smb2_max_write); + if (req->smb2_max_trans) + init_smb2_max_trans_size(req->smb2_max_trans); + if (req->smb2_max_credits) + init_smb2_max_credits(req->smb2_max_credits); + if (req->smbd_max_io_size) + init_smbd_max_io_size(req->smbd_max_io_size); + + if (req->max_connections) + server_conf.max_connections = req->max_connections; + + ret = ksmbd_set_netbios_name(req->netbios_name); + ret |= ksmbd_set_server_string(req->server_string); + ret |= ksmbd_set_work_group(req->work_group); + ret |= ksmbd_tcp_set_interfaces(KSMBD_STARTUP_CONFIG_INTERFACES(req), + req->ifc_list_sz); + if (ret) { + pr_err("Server configuration error: %s %s %s\n", + req->netbios_name, req->server_string, + req->work_group); + return ret; + } + + if (req->min_prot[0]) { + ret = ksmbd_lookup_protocol_idx(req->min_prot); + if (ret >= 0) + server_conf.min_protocol = ret; + } + if (req->max_prot[0]) { + ret = ksmbd_lookup_protocol_idx(req->max_prot); + if (ret >= 0) + server_conf.max_protocol = ret; + } + + if (server_conf.ipc_timeout) + schedule_delayed_work(&ipc_timer_work, server_conf.ipc_timeout); + return 0; +} + +static int handle_startup_event(struct sk_buff *skb, struct genl_info *info) +{ + int ret = 0; + +#ifdef CONFIG_SMB_SERVER_CHECK_CAP_NET_ADMIN + if (!netlink_capable(skb, CAP_NET_ADMIN)) + return -EPERM; +#endif + + if (!ksmbd_ipc_validate_version(info)) + return -EINVAL; + + if (!info->attrs[KSMBD_EVENT_STARTING_UP]) + return -EINVAL; + + mutex_lock(&startup_lock); + if (!ksmbd_server_configurable()) { + mutex_unlock(&startup_lock); + pr_err("Server reset is in progress, can't start daemon\n"); + return -EINVAL; + } + + if (ksmbd_tools_pid) { + if (ksmbd_ipc_heartbeat_request() == 0) { + ret = -EINVAL; + goto out; + } + + pr_err("Reconnect to a new user space daemon\n"); + } else { + struct ksmbd_startup_request *req; + + req = nla_data(info->attrs[info->genlhdr->cmd]); + ret = ipc_server_config_on_startup(req); + if (ret) + goto out; + server_queue_ctrl_init_work(); + } + + ksmbd_tools_pid = info->snd_portid; + ipc_update_last_active(); + +out: + mutex_unlock(&startup_lock); + return ret; +} + +static int handle_unsupported_event(struct sk_buff *skb, struct genl_info *info) +{ + pr_err("Unknown IPC event: %d, ignore.\n", info->genlhdr->cmd); + return -EINVAL; +} + +static int handle_generic_event(struct sk_buff *skb, struct genl_info *info) +{ + void *payload; + int sz; + int type = info->genlhdr->cmd; + +#ifdef CONFIG_SMB_SERVER_CHECK_CAP_NET_ADMIN + if (!netlink_capable(skb, CAP_NET_ADMIN)) + return -EPERM; +#endif + + if (type >= KSMBD_EVENT_MAX) { + WARN_ON(1); + return -EINVAL; + } + + if (!ksmbd_ipc_validate_version(info)) + return -EINVAL; + + if (!info->attrs[type]) + return -EINVAL; + + payload = nla_data(info->attrs[info->genlhdr->cmd]); + sz = nla_len(info->attrs[info->genlhdr->cmd]); + return handle_response(type, payload, sz); +} + +static int ipc_msg_send(struct ksmbd_ipc_msg *msg) +{ + struct genlmsghdr *nlh; + struct sk_buff *skb; + int ret = -EINVAL; + + if (!ksmbd_tools_pid) + return ret; + + skb = genlmsg_new(msg->sz, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + nlh = genlmsg_put(skb, 0, 0, &ksmbd_genl_family, 0, msg->type); + if (!nlh) + goto out; + + ret = nla_put(skb, msg->type, msg->sz, msg->payload); + if (ret) { + genlmsg_cancel(skb, nlh); + goto out; + } + + genlmsg_end(skb, nlh); + ret = genlmsg_unicast(&init_net, skb, ksmbd_tools_pid); + if (!ret) + ipc_update_last_active(); + return ret; + +out: + nlmsg_free(skb); + return ret; +} + +static void *ipc_msg_send_request(struct ksmbd_ipc_msg *msg, unsigned int handle) +{ + struct ipc_msg_table_entry entry; + int ret; + + if ((int)handle < 0) + return NULL; + + entry.type = msg->type; + entry.response = NULL; + init_waitqueue_head(&entry.wait); + + down_write(&ipc_msg_table_lock); + entry.handle = handle; + hash_add(ipc_msg_table, &entry.ipc_table_hlist, entry.handle); + up_write(&ipc_msg_table_lock); + + ret = ipc_msg_send(msg); + if (ret) + goto out; + + ret = wait_event_interruptible_timeout(entry.wait, + entry.response != NULL, + IPC_WAIT_TIMEOUT); +out: + down_write(&ipc_msg_table_lock); + hash_del(&entry.ipc_table_hlist); + up_write(&ipc_msg_table_lock); + return entry.response; +} + +static int ksmbd_ipc_heartbeat_request(void) +{ + struct ksmbd_ipc_msg *msg; + int ret; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_heartbeat)); + if (!msg) + return -EINVAL; + + msg->type = KSMBD_EVENT_HEARTBEAT_REQUEST; + ret = ipc_msg_send(msg); + ipc_msg_free(msg); + return ret; +} + +struct ksmbd_login_response *ksmbd_ipc_login_request(const char *account) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_login_request *req; + struct ksmbd_login_response *resp; + + if (strlen(account) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ) + return NULL; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_login_request)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_LOGIN_REQUEST; + req = (struct ksmbd_login_request *)msg->payload; + req->handle = ksmbd_acquire_id(&ipc_ida); + strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_handle_free(req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_spnego_authen_response * +ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_spnego_authen_request *req; + struct ksmbd_spnego_authen_response *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_spnego_authen_request) + + blob_len + 1); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST; + req = (struct ksmbd_spnego_authen_request *)msg->payload; + req->handle = ksmbd_acquire_id(&ipc_ida); + req->spnego_blob_len = blob_len; + memcpy(req->spnego_blob, spnego_blob, blob_len); + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_handle_free(req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_tree_connect_response * +ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess, + struct ksmbd_share_config *share, + struct ksmbd_tree_connect *tree_conn, + struct sockaddr *peer_addr) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_tree_connect_request *req; + struct ksmbd_tree_connect_response *resp; + + if (strlen(user_name(sess->user)) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ) + return NULL; + + if (strlen(share->name) >= KSMBD_REQ_MAX_SHARE_NAME) + return NULL; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_tree_connect_request)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_TREE_CONNECT_REQUEST; + req = (struct ksmbd_tree_connect_request *)msg->payload; + + req->handle = ksmbd_acquire_id(&ipc_ida); + req->account_flags = sess->user->flags; + req->session_id = sess->id; + req->connect_id = tree_conn->id; + strscpy(req->account, user_name(sess->user), KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); + strscpy(req->share, share->name, KSMBD_REQ_MAX_SHARE_NAME); + snprintf(req->peer_addr, sizeof(req->peer_addr), "%pIS", peer_addr); + + if (peer_addr->sa_family == AF_INET6) + req->flags |= KSMBD_TREE_CONN_FLAG_REQUEST_IPV6; + if (test_session_flag(sess, CIFDS_SESSION_FLAG_SMB2)) + req->flags |= KSMBD_TREE_CONN_FLAG_REQUEST_SMB2; + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_handle_free(req->handle); + ipc_msg_free(msg); + return resp; +} + +int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id, + unsigned long long connect_id) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_tree_disconnect_request *req; + int ret; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_tree_disconnect_request)); + if (!msg) + return -ENOMEM; + + msg->type = KSMBD_EVENT_TREE_DISCONNECT_REQUEST; + req = (struct ksmbd_tree_disconnect_request *)msg->payload; + req->session_id = session_id; + req->connect_id = connect_id; + + ret = ipc_msg_send(msg); + ipc_msg_free(msg); + return ret; +} + +int ksmbd_ipc_logout_request(const char *account, int flags) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_logout_request *req; + int ret; + + if (strlen(account) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ) + return -EINVAL; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_logout_request)); + if (!msg) + return -ENOMEM; + + msg->type = KSMBD_EVENT_LOGOUT_REQUEST; + req = (struct ksmbd_logout_request *)msg->payload; + req->account_flags = flags; + strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ); + + ret = ipc_msg_send(msg); + ipc_msg_free(msg); + return ret; +} + +struct ksmbd_share_config_response * +ksmbd_ipc_share_config_request(const char *name) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_share_config_request *req; + struct ksmbd_share_config_response *resp; + + if (strlen(name) >= KSMBD_REQ_MAX_SHARE_NAME) + return NULL; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_share_config_request)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_SHARE_CONFIG_REQUEST; + req = (struct ksmbd_share_config_request *)msg->payload; + req->handle = ksmbd_acquire_id(&ipc_ida); + strscpy(req->share_name, name, KSMBD_REQ_MAX_SHARE_NAME); + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_handle_free(req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_open(struct ksmbd_session *sess, int handle) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = (struct ksmbd_rpc_command *)msg->payload; + req->handle = handle; + req->flags = ksmbd_session_rpc_method(sess, handle); + req->flags |= KSMBD_RPC_OPEN_METHOD; + req->payload_sz = 0; + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_close(struct ksmbd_session *sess, int handle) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = (struct ksmbd_rpc_command *)msg->payload; + req->handle = handle; + req->flags = ksmbd_session_rpc_method(sess, handle); + req->flags |= KSMBD_RPC_CLOSE_METHOD; + req->payload_sz = 0; + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle, + void *payload, size_t payload_sz) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = (struct ksmbd_rpc_command *)msg->payload; + req->handle = handle; + req->flags = ksmbd_session_rpc_method(sess, handle); + req->flags |= rpc_context_flags(sess); + req->flags |= KSMBD_RPC_WRITE_METHOD; + req->payload_sz = payload_sz; + memcpy(req->payload, payload, payload_sz); + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command)); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = (struct ksmbd_rpc_command *)msg->payload; + req->handle = handle; + req->flags = ksmbd_session_rpc_method(sess, handle); + req->flags |= rpc_context_flags(sess); + req->flags |= KSMBD_RPC_READ_METHOD; + req->payload_sz = 0; + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle, + void *payload, size_t payload_sz) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = (struct ksmbd_rpc_command *)msg->payload; + req->handle = handle; + req->flags = ksmbd_session_rpc_method(sess, handle); + req->flags |= rpc_context_flags(sess); + req->flags |= KSMBD_RPC_IOCTL_METHOD; + req->payload_sz = payload_sz; + memcpy(req->payload, payload, payload_sz); + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_free(msg); + return resp; +} + +struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, void *payload, + size_t payload_sz) +{ + struct ksmbd_ipc_msg *msg; + struct ksmbd_rpc_command *req; + struct ksmbd_rpc_command *resp; + + msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1); + if (!msg) + return NULL; + + msg->type = KSMBD_EVENT_RPC_REQUEST; + req = (struct ksmbd_rpc_command *)msg->payload; + req->handle = ksmbd_acquire_id(&ipc_ida); + req->flags = rpc_context_flags(sess); + req->flags |= KSMBD_RPC_RAP_METHOD; + req->payload_sz = payload_sz; + memcpy(req->payload, payload, payload_sz); + + resp = ipc_msg_send_request(msg, req->handle); + ipc_msg_handle_free(req->handle); + ipc_msg_free(msg); + return resp; +} + +static int __ipc_heartbeat(void) +{ + unsigned long delta; + + if (!ksmbd_server_running()) + return 0; + + if (time_after(jiffies, server_conf.ipc_last_active)) { + delta = (jiffies - server_conf.ipc_last_active); + } else { + ipc_update_last_active(); + schedule_delayed_work(&ipc_timer_work, + server_conf.ipc_timeout); + return 0; + } + + if (delta < server_conf.ipc_timeout) { + schedule_delayed_work(&ipc_timer_work, + server_conf.ipc_timeout - delta); + return 0; + } + + if (ksmbd_ipc_heartbeat_request() == 0) { + schedule_delayed_work(&ipc_timer_work, + server_conf.ipc_timeout); + return 0; + } + + mutex_lock(&startup_lock); + WRITE_ONCE(server_conf.state, SERVER_STATE_RESETTING); + server_conf.ipc_last_active = 0; + ksmbd_tools_pid = 0; + pr_err("No IPC daemon response for %lus\n", delta / HZ); + mutex_unlock(&startup_lock); + return -EINVAL; +} + +static void ipc_timer_heartbeat(struct work_struct *w) +{ + if (__ipc_heartbeat()) + server_queue_ctrl_reset_work(); +} + +int ksmbd_ipc_id_alloc(void) +{ + return ksmbd_acquire_id(&ipc_ida); +} + +void ksmbd_rpc_id_free(int handle) +{ + ksmbd_release_id(&ipc_ida, handle); +} + +void ksmbd_ipc_release(void) +{ + cancel_delayed_work_sync(&ipc_timer_work); + genl_unregister_family(&ksmbd_genl_family); +} + +void ksmbd_ipc_soft_reset(void) +{ + mutex_lock(&startup_lock); + ksmbd_tools_pid = 0; + cancel_delayed_work_sync(&ipc_timer_work); + mutex_unlock(&startup_lock); +} + +int ksmbd_ipc_init(void) +{ + int ret = 0; + + ksmbd_nl_init_fixup(); + INIT_DELAYED_WORK(&ipc_timer_work, ipc_timer_heartbeat); + + ret = genl_register_family(&ksmbd_genl_family); + if (ret) { + pr_err("Failed to register KSMBD netlink interface %d\n", ret); + cancel_delayed_work_sync(&ipc_timer_work); + } + + return ret; +} diff --git a/fs/ksmbd/transport_ipc.h b/fs/ksmbd/transport_ipc.h new file mode 100644 index 0000000000000..5e5b90a0c1879 --- /dev/null +++ b/fs/ksmbd/transport_ipc.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_TRANSPORT_IPC_H__ +#define __KSMBD_TRANSPORT_IPC_H__ + +#include + +#define KSMBD_IPC_MAX_PAYLOAD 4096 + +struct ksmbd_login_response * +ksmbd_ipc_login_request(const char *account); + +struct ksmbd_session; +struct ksmbd_share_config; +struct ksmbd_tree_connect; +struct sockaddr; + +struct ksmbd_tree_connect_response * +ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess, + struct ksmbd_share_config *share, + struct ksmbd_tree_connect *tree_conn, + struct sockaddr *peer_addr); +int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id, + unsigned long long connect_id); +int ksmbd_ipc_logout_request(const char *account, int flags); +struct ksmbd_share_config_response * +ksmbd_ipc_share_config_request(const char *name); +struct ksmbd_spnego_authen_response * +ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len); +int ksmbd_ipc_id_alloc(void); +void ksmbd_rpc_id_free(int handle); +struct ksmbd_rpc_command *ksmbd_rpc_open(struct ksmbd_session *sess, int handle); +struct ksmbd_rpc_command *ksmbd_rpc_close(struct ksmbd_session *sess, int handle); +struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle, + void *payload, size_t payload_sz); +struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle); +struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle, + void *payload, size_t payload_sz); +struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, void *payload, + size_t payload_sz); +void ksmbd_ipc_release(void); +void ksmbd_ipc_soft_reset(void); +int ksmbd_ipc_init(void); +#endif /* __KSMBD_TRANSPORT_IPC_H__ */ diff --git a/fs/ksmbd/transport_rdma.c b/fs/ksmbd/transport_rdma.c new file mode 100644 index 0000000000000..c34c85ba2d2f3 --- /dev/null +++ b/fs/ksmbd/transport_rdma.c @@ -0,0 +1,2297 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2017, Microsoft Corporation. + * Copyright (C) 2018, LG Electronics. + * + * Author(s): Long Li , + * Hyunchul Lee + */ + +#define SUBMOD_NAME "smb_direct" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "glob.h" +#include "connection.h" +#include "smb_common.h" +#include "smbstatus.h" +#include "transport_rdma.h" + +#define SMB_DIRECT_PORT_IWARP 5445 +#define SMB_DIRECT_PORT_INFINIBAND 445 + +#define SMB_DIRECT_VERSION_LE cpu_to_le16(0x0100) + +/* SMB_DIRECT negotiation timeout in seconds */ +#define SMB_DIRECT_NEGOTIATE_TIMEOUT 120 + +#define SMB_DIRECT_MAX_SEND_SGES 6 +#define SMB_DIRECT_MAX_RECV_SGES 1 + +/* + * Default maximum number of RDMA read/write outstanding on this connection + * This value is possibly decreased during QP creation on hardware limit + */ +#define SMB_DIRECT_CM_INITIATOR_DEPTH 8 + +/* Maximum number of retries on data transfer operations */ +#define SMB_DIRECT_CM_RETRY 6 +/* No need to retry on Receiver Not Ready since SMB_DIRECT manages credits */ +#define SMB_DIRECT_CM_RNR_RETRY 0 + +/* + * User configurable initial values per SMB_DIRECT transport connection + * as defined in [MS-SMBD] 3.1.1.1 + * Those may change after a SMB_DIRECT negotiation + */ + +/* Set 445 port to SMB Direct port by default */ +static int smb_direct_port = SMB_DIRECT_PORT_INFINIBAND; + +/* The local peer's maximum number of credits to grant to the peer */ +static int smb_direct_receive_credit_max = 255; + +/* The remote peer's credit request of local peer */ +static int smb_direct_send_credit_target = 255; + +/* The maximum single message size can be sent to remote peer */ +static int smb_direct_max_send_size = 1364; + +/* The maximum fragmented upper-layer payload receive size supported */ +static int smb_direct_max_fragmented_recv_size = 1024 * 1024; + +/* The maximum single-message size which can be received */ +static int smb_direct_max_receive_size = 1364; + +static int smb_direct_max_read_write_size = SMBD_DEFAULT_IOSIZE; + +static LIST_HEAD(smb_direct_device_list); +static DEFINE_RWLOCK(smb_direct_device_lock); + +struct smb_direct_device { + struct ib_device *ib_dev; + struct list_head list; +}; + +static struct smb_direct_listener { + struct rdma_cm_id *cm_id; +} smb_direct_listener; + +static struct workqueue_struct *smb_direct_wq; + +enum smb_direct_status { + SMB_DIRECT_CS_NEW = 0, + SMB_DIRECT_CS_CONNECTED, + SMB_DIRECT_CS_DISCONNECTING, + SMB_DIRECT_CS_DISCONNECTED, +}; + +struct smb_direct_transport { + struct ksmbd_transport transport; + + enum smb_direct_status status; + bool full_packet_received; + wait_queue_head_t wait_status; + + struct rdma_cm_id *cm_id; + struct ib_cq *send_cq; + struct ib_cq *recv_cq; + struct ib_pd *pd; + struct ib_qp *qp; + + int max_send_size; + int max_recv_size; + int max_fragmented_send_size; + int max_fragmented_recv_size; + int max_rdma_rw_size; + + spinlock_t reassembly_queue_lock; + struct list_head reassembly_queue; + int reassembly_data_length; + int reassembly_queue_length; + int first_entry_offset; + wait_queue_head_t wait_reassembly_queue; + + spinlock_t receive_credit_lock; + int recv_credits; + int count_avail_recvmsg; + int recv_credit_max; + int recv_credit_target; + + spinlock_t recvmsg_queue_lock; + struct list_head recvmsg_queue; + + spinlock_t empty_recvmsg_queue_lock; + struct list_head empty_recvmsg_queue; + + int send_credit_target; + atomic_t send_credits; + spinlock_t lock_new_recv_credits; + int new_recv_credits; + int max_rw_credits; + int pages_per_rw_credit; + atomic_t rw_credits; + + wait_queue_head_t wait_send_credits; + wait_queue_head_t wait_rw_credits; + + mempool_t *sendmsg_mempool; + struct kmem_cache *sendmsg_cache; + mempool_t *recvmsg_mempool; + struct kmem_cache *recvmsg_cache; + + wait_queue_head_t wait_send_pending; + atomic_t send_pending; + + struct delayed_work post_recv_credits_work; + struct work_struct send_immediate_work; + struct work_struct disconnect_work; + + bool negotiation_requested; +}; + +#define KSMBD_TRANS(t) ((struct ksmbd_transport *)&((t)->transport)) + +enum { + SMB_DIRECT_MSG_NEGOTIATE_REQ = 0, + SMB_DIRECT_MSG_DATA_TRANSFER +}; + +static struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops; + +struct smb_direct_send_ctx { + struct list_head msg_list; + int wr_cnt; + bool need_invalidate_rkey; + unsigned int remote_key; +}; + +struct smb_direct_sendmsg { + struct smb_direct_transport *transport; + struct ib_send_wr wr; + struct list_head list; + int num_sge; + struct ib_sge sge[SMB_DIRECT_MAX_SEND_SGES]; + struct ib_cqe cqe; + u8 packet[]; +}; + +struct smb_direct_recvmsg { + struct smb_direct_transport *transport; + struct list_head list; + int type; + struct ib_sge sge; + struct ib_cqe cqe; + bool first_segment; + u8 packet[]; +}; + +struct smb_direct_rdma_rw_msg { + struct smb_direct_transport *t; + struct ib_cqe cqe; + int status; + struct completion *completion; + struct list_head list; + struct rdma_rw_ctx rw_ctx; + struct sg_table sgt; + struct scatterlist sg_list[0]; +}; + +void init_smbd_max_io_size(unsigned int sz) +{ + sz = clamp_val(sz, SMBD_MIN_IOSIZE, SMBD_MAX_IOSIZE); + smb_direct_max_read_write_size = sz; +} + +unsigned int get_smbd_max_read_write_size(void) +{ + return smb_direct_max_read_write_size; +} + +static inline int get_buf_page_count(void *buf, int size) +{ + return (int)(DIV_ROUND_UP((uintptr_t)buf + size, PAGE_SIZE) - + (uintptr_t)buf / PAGE_SIZE); +} + +static void smb_direct_destroy_pools(struct smb_direct_transport *transport); +static void smb_direct_post_recv_credits(struct work_struct *work); +static int smb_direct_post_send_data(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx, + struct kvec *iov, int niov, + int remaining_data_length); + +static inline struct smb_direct_transport * +smb_trans_direct_transfort(struct ksmbd_transport *t) +{ + return container_of(t, struct smb_direct_transport, transport); +} + +static inline void +*smb_direct_recvmsg_payload(struct smb_direct_recvmsg *recvmsg) +{ + return (void *)recvmsg->packet; +} + +static inline bool is_receive_credit_post_required(int receive_credits, + int avail_recvmsg_count) +{ + return receive_credits <= (smb_direct_receive_credit_max >> 3) && + avail_recvmsg_count >= (receive_credits >> 2); +} + +static struct +smb_direct_recvmsg *get_free_recvmsg(struct smb_direct_transport *t) +{ + struct smb_direct_recvmsg *recvmsg = NULL; + + spin_lock(&t->recvmsg_queue_lock); + if (!list_empty(&t->recvmsg_queue)) { + recvmsg = list_first_entry(&t->recvmsg_queue, + struct smb_direct_recvmsg, + list); + list_del(&recvmsg->list); + } + spin_unlock(&t->recvmsg_queue_lock); + return recvmsg; +} + +static void put_recvmsg(struct smb_direct_transport *t, + struct smb_direct_recvmsg *recvmsg) +{ + ib_dma_unmap_single(t->cm_id->device, recvmsg->sge.addr, + recvmsg->sge.length, DMA_FROM_DEVICE); + + spin_lock(&t->recvmsg_queue_lock); + list_add(&recvmsg->list, &t->recvmsg_queue); + spin_unlock(&t->recvmsg_queue_lock); +} + +static struct +smb_direct_recvmsg *get_empty_recvmsg(struct smb_direct_transport *t) +{ + struct smb_direct_recvmsg *recvmsg = NULL; + + spin_lock(&t->empty_recvmsg_queue_lock); + if (!list_empty(&t->empty_recvmsg_queue)) { + recvmsg = list_first_entry(&t->empty_recvmsg_queue, + struct smb_direct_recvmsg, list); + list_del(&recvmsg->list); + } + spin_unlock(&t->empty_recvmsg_queue_lock); + return recvmsg; +} + +static void put_empty_recvmsg(struct smb_direct_transport *t, + struct smb_direct_recvmsg *recvmsg) +{ + ib_dma_unmap_single(t->cm_id->device, recvmsg->sge.addr, + recvmsg->sge.length, DMA_FROM_DEVICE); + + spin_lock(&t->empty_recvmsg_queue_lock); + list_add_tail(&recvmsg->list, &t->empty_recvmsg_queue); + spin_unlock(&t->empty_recvmsg_queue_lock); +} + +static void enqueue_reassembly(struct smb_direct_transport *t, + struct smb_direct_recvmsg *recvmsg, + int data_length) +{ + spin_lock(&t->reassembly_queue_lock); + list_add_tail(&recvmsg->list, &t->reassembly_queue); + t->reassembly_queue_length++; + /* + * Make sure reassembly_data_length is updated after list and + * reassembly_queue_length are updated. On the dequeue side + * reassembly_data_length is checked without a lock to determine + * if reassembly_queue_length and list is up to date + */ + virt_wmb(); + t->reassembly_data_length += data_length; + spin_unlock(&t->reassembly_queue_lock); +} + +static struct smb_direct_recvmsg *get_first_reassembly(struct smb_direct_transport *t) +{ + if (!list_empty(&t->reassembly_queue)) + return list_first_entry(&t->reassembly_queue, + struct smb_direct_recvmsg, list); + else + return NULL; +} + +static void smb_direct_disconnect_rdma_work(struct work_struct *work) +{ + struct smb_direct_transport *t = + container_of(work, struct smb_direct_transport, + disconnect_work); + + if (t->status == SMB_DIRECT_CS_CONNECTED) { + t->status = SMB_DIRECT_CS_DISCONNECTING; + rdma_disconnect(t->cm_id); + } +} + +static void +smb_direct_disconnect_rdma_connection(struct smb_direct_transport *t) +{ + if (t->status == SMB_DIRECT_CS_CONNECTED) + queue_work(smb_direct_wq, &t->disconnect_work); +} + +static void smb_direct_send_immediate_work(struct work_struct *work) +{ + struct smb_direct_transport *t = container_of(work, + struct smb_direct_transport, send_immediate_work); + + if (t->status != SMB_DIRECT_CS_CONNECTED) + return; + + smb_direct_post_send_data(t, NULL, NULL, 0, 0); +} + +static struct smb_direct_transport *alloc_transport(struct rdma_cm_id *cm_id) +{ + struct smb_direct_transport *t; + struct ksmbd_conn *conn; + + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (!t) + return NULL; + + t->cm_id = cm_id; + cm_id->context = t; + + t->status = SMB_DIRECT_CS_NEW; + init_waitqueue_head(&t->wait_status); + + spin_lock_init(&t->reassembly_queue_lock); + INIT_LIST_HEAD(&t->reassembly_queue); + t->reassembly_data_length = 0; + t->reassembly_queue_length = 0; + init_waitqueue_head(&t->wait_reassembly_queue); + init_waitqueue_head(&t->wait_send_credits); + init_waitqueue_head(&t->wait_rw_credits); + + spin_lock_init(&t->receive_credit_lock); + spin_lock_init(&t->recvmsg_queue_lock); + INIT_LIST_HEAD(&t->recvmsg_queue); + + spin_lock_init(&t->empty_recvmsg_queue_lock); + INIT_LIST_HEAD(&t->empty_recvmsg_queue); + + init_waitqueue_head(&t->wait_send_pending); + atomic_set(&t->send_pending, 0); + + spin_lock_init(&t->lock_new_recv_credits); + + INIT_DELAYED_WORK(&t->post_recv_credits_work, + smb_direct_post_recv_credits); + INIT_WORK(&t->send_immediate_work, smb_direct_send_immediate_work); + INIT_WORK(&t->disconnect_work, smb_direct_disconnect_rdma_work); + + conn = ksmbd_conn_alloc(); + if (!conn) + goto err; + conn->transport = KSMBD_TRANS(t); + KSMBD_TRANS(t)->conn = conn; + KSMBD_TRANS(t)->ops = &ksmbd_smb_direct_transport_ops; + return t; +err: + kfree(t); + return NULL; +} + +static void free_transport(struct smb_direct_transport *t) +{ + struct smb_direct_recvmsg *recvmsg; + + wake_up_interruptible(&t->wait_send_credits); + + ksmbd_debug(RDMA, "wait for all send posted to IB to finish\n"); + wait_event(t->wait_send_pending, + atomic_read(&t->send_pending) == 0); + + cancel_work_sync(&t->disconnect_work); + cancel_delayed_work_sync(&t->post_recv_credits_work); + cancel_work_sync(&t->send_immediate_work); + + if (t->qp) { + ib_drain_qp(t->qp); + ib_mr_pool_destroy(t->qp, &t->qp->rdma_mrs); + ib_destroy_qp(t->qp); + } + + ksmbd_debug(RDMA, "drain the reassembly queue\n"); + do { + spin_lock(&t->reassembly_queue_lock); + recvmsg = get_first_reassembly(t); + if (recvmsg) { + list_del(&recvmsg->list); + spin_unlock(&t->reassembly_queue_lock); + put_recvmsg(t, recvmsg); + } else { + spin_unlock(&t->reassembly_queue_lock); + } + } while (recvmsg); + t->reassembly_data_length = 0; + + if (t->send_cq) + ib_free_cq(t->send_cq); + if (t->recv_cq) + ib_free_cq(t->recv_cq); + if (t->pd) + ib_dealloc_pd(t->pd); + if (t->cm_id) + rdma_destroy_id(t->cm_id); + + smb_direct_destroy_pools(t); + ksmbd_conn_free(KSMBD_TRANS(t)->conn); + kfree(t); +} + +static struct smb_direct_sendmsg +*smb_direct_alloc_sendmsg(struct smb_direct_transport *t) +{ + struct smb_direct_sendmsg *msg; + + msg = mempool_alloc(t->sendmsg_mempool, GFP_KERNEL); + if (!msg) + return ERR_PTR(-ENOMEM); + msg->transport = t; + INIT_LIST_HEAD(&msg->list); + msg->num_sge = 0; + return msg; +} + +static void smb_direct_free_sendmsg(struct smb_direct_transport *t, + struct smb_direct_sendmsg *msg) +{ + int i; + + if (msg->num_sge > 0) { + ib_dma_unmap_single(t->cm_id->device, + msg->sge[0].addr, msg->sge[0].length, + DMA_TO_DEVICE); + for (i = 1; i < msg->num_sge; i++) + ib_dma_unmap_page(t->cm_id->device, + msg->sge[i].addr, msg->sge[i].length, + DMA_TO_DEVICE); + } + mempool_free(msg, t->sendmsg_mempool); +} + +static int smb_direct_check_recvmsg(struct smb_direct_recvmsg *recvmsg) +{ + switch (recvmsg->type) { + case SMB_DIRECT_MSG_DATA_TRANSFER: { + struct smb_direct_data_transfer *req = + (struct smb_direct_data_transfer *)recvmsg->packet; + struct smb2_hdr *hdr = (struct smb2_hdr *)(recvmsg->packet + + le32_to_cpu(req->data_offset)); + ksmbd_debug(RDMA, + "CreditGranted: %u, CreditRequested: %u, DataLength: %u, RemainingDataLength: %u, SMB: %x, Command: %u\n", + le16_to_cpu(req->credits_granted), + le16_to_cpu(req->credits_requested), + req->data_length, req->remaining_data_length, + hdr->ProtocolId, hdr->Command); + break; + } + case SMB_DIRECT_MSG_NEGOTIATE_REQ: { + struct smb_direct_negotiate_req *req = + (struct smb_direct_negotiate_req *)recvmsg->packet; + ksmbd_debug(RDMA, + "MinVersion: %u, MaxVersion: %u, CreditRequested: %u, MaxSendSize: %u, MaxRecvSize: %u, MaxFragmentedSize: %u\n", + le16_to_cpu(req->min_version), + le16_to_cpu(req->max_version), + le16_to_cpu(req->credits_requested), + le32_to_cpu(req->preferred_send_size), + le32_to_cpu(req->max_receive_size), + le32_to_cpu(req->max_fragmented_size)); + if (le16_to_cpu(req->min_version) > 0x0100 || + le16_to_cpu(req->max_version) < 0x0100) + return -EOPNOTSUPP; + if (le16_to_cpu(req->credits_requested) <= 0 || + le32_to_cpu(req->max_receive_size) <= 128 || + le32_to_cpu(req->max_fragmented_size) <= + 128 * 1024) + return -ECONNABORTED; + + break; + } + default: + return -EINVAL; + } + return 0; +} + +static void recv_done(struct ib_cq *cq, struct ib_wc *wc) +{ + struct smb_direct_recvmsg *recvmsg; + struct smb_direct_transport *t; + + recvmsg = container_of(wc->wr_cqe, struct smb_direct_recvmsg, cqe); + t = recvmsg->transport; + + if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_RECV) { + if (wc->status != IB_WC_WR_FLUSH_ERR) { + pr_err("Recv error. status='%s (%d)' opcode=%d\n", + ib_wc_status_msg(wc->status), wc->status, + wc->opcode); + smb_direct_disconnect_rdma_connection(t); + } + put_empty_recvmsg(t, recvmsg); + return; + } + + ksmbd_debug(RDMA, "Recv completed. status='%s (%d)', opcode=%d\n", + ib_wc_status_msg(wc->status), wc->status, + wc->opcode); + + ib_dma_sync_single_for_cpu(wc->qp->device, recvmsg->sge.addr, + recvmsg->sge.length, DMA_FROM_DEVICE); + + switch (recvmsg->type) { + case SMB_DIRECT_MSG_NEGOTIATE_REQ: + if (wc->byte_len < sizeof(struct smb_direct_negotiate_req)) { + put_empty_recvmsg(t, recvmsg); + return; + } + t->negotiation_requested = true; + t->full_packet_received = true; + t->status = SMB_DIRECT_CS_CONNECTED; + enqueue_reassembly(t, recvmsg, 0); + wake_up_interruptible(&t->wait_status); + break; + case SMB_DIRECT_MSG_DATA_TRANSFER: { + struct smb_direct_data_transfer *data_transfer = + (struct smb_direct_data_transfer *)recvmsg->packet; + unsigned int data_length; + int avail_recvmsg_count, receive_credits; + + if (wc->byte_len < + offsetof(struct smb_direct_data_transfer, padding)) { + put_empty_recvmsg(t, recvmsg); + return; + } + + data_length = le32_to_cpu(data_transfer->data_length); + if (data_length) { + if (wc->byte_len < sizeof(struct smb_direct_data_transfer) + + (u64)data_length) { + put_empty_recvmsg(t, recvmsg); + return; + } + + if (t->full_packet_received) + recvmsg->first_segment = true; + + if (le32_to_cpu(data_transfer->remaining_data_length)) + t->full_packet_received = false; + else + t->full_packet_received = true; + + enqueue_reassembly(t, recvmsg, (int)data_length); + wake_up_interruptible(&t->wait_reassembly_queue); + + spin_lock(&t->receive_credit_lock); + receive_credits = --(t->recv_credits); + avail_recvmsg_count = t->count_avail_recvmsg; + spin_unlock(&t->receive_credit_lock); + } else { + put_empty_recvmsg(t, recvmsg); + + spin_lock(&t->receive_credit_lock); + receive_credits = --(t->recv_credits); + avail_recvmsg_count = ++(t->count_avail_recvmsg); + spin_unlock(&t->receive_credit_lock); + } + + t->recv_credit_target = + le16_to_cpu(data_transfer->credits_requested); + atomic_add(le16_to_cpu(data_transfer->credits_granted), + &t->send_credits); + + if (le16_to_cpu(data_transfer->flags) & + SMB_DIRECT_RESPONSE_REQUESTED) + queue_work(smb_direct_wq, &t->send_immediate_work); + + if (atomic_read(&t->send_credits) > 0) + wake_up_interruptible(&t->wait_send_credits); + + if (is_receive_credit_post_required(receive_credits, avail_recvmsg_count)) + mod_delayed_work(smb_direct_wq, + &t->post_recv_credits_work, 0); + break; + } + default: + break; + } +} + +static int smb_direct_post_recv(struct smb_direct_transport *t, + struct smb_direct_recvmsg *recvmsg) +{ + struct ib_recv_wr wr; + int ret; + + recvmsg->sge.addr = ib_dma_map_single(t->cm_id->device, + recvmsg->packet, t->max_recv_size, + DMA_FROM_DEVICE); + ret = ib_dma_mapping_error(t->cm_id->device, recvmsg->sge.addr); + if (ret) + return ret; + recvmsg->sge.length = t->max_recv_size; + recvmsg->sge.lkey = t->pd->local_dma_lkey; + recvmsg->cqe.done = recv_done; + + wr.wr_cqe = &recvmsg->cqe; + wr.next = NULL; + wr.sg_list = &recvmsg->sge; + wr.num_sge = 1; + + ret = ib_post_recv(t->qp, &wr, NULL); + if (ret) { + pr_err("Can't post recv: %d\n", ret); + ib_dma_unmap_single(t->cm_id->device, + recvmsg->sge.addr, recvmsg->sge.length, + DMA_FROM_DEVICE); + smb_direct_disconnect_rdma_connection(t); + return ret; + } + return ret; +} + +static int smb_direct_read(struct ksmbd_transport *t, char *buf, + unsigned int size) +{ + struct smb_direct_recvmsg *recvmsg; + struct smb_direct_data_transfer *data_transfer; + int to_copy, to_read, data_read, offset; + u32 data_length, remaining_data_length, data_offset; + int rc; + struct smb_direct_transport *st = smb_trans_direct_transfort(t); + +again: + if (st->status != SMB_DIRECT_CS_CONNECTED) { + pr_err("disconnected\n"); + return -ENOTCONN; + } + + /* + * No need to hold the reassembly queue lock all the time as we are + * the only one reading from the front of the queue. The transport + * may add more entries to the back of the queue at the same time + */ + if (st->reassembly_data_length >= size) { + int queue_length; + int queue_removed = 0; + + /* + * Need to make sure reassembly_data_length is read before + * reading reassembly_queue_length and calling + * get_first_reassembly. This call is lock free + * as we never read at the end of the queue which are being + * updated in SOFTIRQ as more data is received + */ + virt_rmb(); + queue_length = st->reassembly_queue_length; + data_read = 0; + to_read = size; + offset = st->first_entry_offset; + while (data_read < size) { + recvmsg = get_first_reassembly(st); + data_transfer = smb_direct_recvmsg_payload(recvmsg); + data_length = le32_to_cpu(data_transfer->data_length); + remaining_data_length = + le32_to_cpu(data_transfer->remaining_data_length); + data_offset = le32_to_cpu(data_transfer->data_offset); + + /* + * The upper layer expects RFC1002 length at the + * beginning of the payload. Return it to indicate + * the total length of the packet. This minimize the + * change to upper layer packet processing logic. This + * will be eventually remove when an intermediate + * transport layer is added + */ + if (recvmsg->first_segment && size == 4) { + unsigned int rfc1002_len = + data_length + remaining_data_length; + *((__be32 *)buf) = cpu_to_be32(rfc1002_len); + data_read = 4; + recvmsg->first_segment = false; + ksmbd_debug(RDMA, + "returning rfc1002 length %d\n", + rfc1002_len); + goto read_rfc1002_done; + } + + to_copy = min_t(int, data_length - offset, to_read); + memcpy(buf + data_read, (char *)data_transfer + data_offset + offset, + to_copy); + + /* move on to the next buffer? */ + if (to_copy == data_length - offset) { + queue_length--; + /* + * No need to lock if we are not at the + * end of the queue + */ + if (queue_length) { + list_del(&recvmsg->list); + } else { + spin_lock_irq(&st->reassembly_queue_lock); + list_del(&recvmsg->list); + spin_unlock_irq(&st->reassembly_queue_lock); + } + queue_removed++; + put_recvmsg(st, recvmsg); + offset = 0; + } else { + offset += to_copy; + } + + to_read -= to_copy; + data_read += to_copy; + } + + spin_lock_irq(&st->reassembly_queue_lock); + st->reassembly_data_length -= data_read; + st->reassembly_queue_length -= queue_removed; + spin_unlock_irq(&st->reassembly_queue_lock); + + spin_lock(&st->receive_credit_lock); + st->count_avail_recvmsg += queue_removed; + if (is_receive_credit_post_required(st->recv_credits, st->count_avail_recvmsg)) { + spin_unlock(&st->receive_credit_lock); + mod_delayed_work(smb_direct_wq, + &st->post_recv_credits_work, 0); + } else { + spin_unlock(&st->receive_credit_lock); + } + + st->first_entry_offset = offset; + ksmbd_debug(RDMA, + "returning to thread data_read=%d reassembly_data_length=%d first_entry_offset=%d\n", + data_read, st->reassembly_data_length, + st->first_entry_offset); +read_rfc1002_done: + return data_read; + } + + ksmbd_debug(RDMA, "wait_event on more data\n"); + rc = wait_event_interruptible(st->wait_reassembly_queue, + st->reassembly_data_length >= size || + st->status != SMB_DIRECT_CS_CONNECTED); + if (rc) + return -EINTR; + + goto again; +} + +static void smb_direct_post_recv_credits(struct work_struct *work) +{ + struct smb_direct_transport *t = container_of(work, + struct smb_direct_transport, post_recv_credits_work.work); + struct smb_direct_recvmsg *recvmsg; + int receive_credits, credits = 0; + int ret; + int use_free = 1; + + spin_lock(&t->receive_credit_lock); + receive_credits = t->recv_credits; + spin_unlock(&t->receive_credit_lock); + + if (receive_credits < t->recv_credit_target) { + while (true) { + if (use_free) + recvmsg = get_free_recvmsg(t); + else + recvmsg = get_empty_recvmsg(t); + if (!recvmsg) { + if (use_free) { + use_free = 0; + continue; + } else { + break; + } + } + + recvmsg->type = SMB_DIRECT_MSG_DATA_TRANSFER; + recvmsg->first_segment = false; + + ret = smb_direct_post_recv(t, recvmsg); + if (ret) { + pr_err("Can't post recv: %d\n", ret); + put_recvmsg(t, recvmsg); + break; + } + credits++; + } + } + + spin_lock(&t->receive_credit_lock); + t->recv_credits += credits; + t->count_avail_recvmsg -= credits; + spin_unlock(&t->receive_credit_lock); + + spin_lock(&t->lock_new_recv_credits); + t->new_recv_credits += credits; + spin_unlock(&t->lock_new_recv_credits); + + if (credits) + queue_work(smb_direct_wq, &t->send_immediate_work); +} + +static void send_done(struct ib_cq *cq, struct ib_wc *wc) +{ + struct smb_direct_sendmsg *sendmsg, *sibling; + struct smb_direct_transport *t; + struct list_head *pos, *prev, *end; + + sendmsg = container_of(wc->wr_cqe, struct smb_direct_sendmsg, cqe); + t = sendmsg->transport; + + ksmbd_debug(RDMA, "Send completed. status='%s (%d)', opcode=%d\n", + ib_wc_status_msg(wc->status), wc->status, + wc->opcode); + + if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) { + pr_err("Send error. status='%s (%d)', opcode=%d\n", + ib_wc_status_msg(wc->status), wc->status, + wc->opcode); + smb_direct_disconnect_rdma_connection(t); + } + + if (atomic_dec_and_test(&t->send_pending)) + wake_up(&t->wait_send_pending); + + /* iterate and free the list of messages in reverse. the list's head + * is invalid. + */ + for (pos = &sendmsg->list, prev = pos->prev, end = sendmsg->list.next; + prev != end; pos = prev, prev = prev->prev) { + sibling = container_of(pos, struct smb_direct_sendmsg, list); + smb_direct_free_sendmsg(t, sibling); + } + + sibling = container_of(pos, struct smb_direct_sendmsg, list); + smb_direct_free_sendmsg(t, sibling); +} + +static int manage_credits_prior_sending(struct smb_direct_transport *t) +{ + int new_credits; + + spin_lock(&t->lock_new_recv_credits); + new_credits = t->new_recv_credits; + t->new_recv_credits = 0; + spin_unlock(&t->lock_new_recv_credits); + + return new_credits; +} + +static int smb_direct_post_send(struct smb_direct_transport *t, + struct ib_send_wr *wr) +{ + int ret; + + atomic_inc(&t->send_pending); + ret = ib_post_send(t->qp, wr, NULL); + if (ret) { + pr_err("failed to post send: %d\n", ret); + if (atomic_dec_and_test(&t->send_pending)) + wake_up(&t->wait_send_pending); + smb_direct_disconnect_rdma_connection(t); + } + return ret; +} + +static void smb_direct_send_ctx_init(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx, + bool need_invalidate_rkey, + unsigned int remote_key) +{ + INIT_LIST_HEAD(&send_ctx->msg_list); + send_ctx->wr_cnt = 0; + send_ctx->need_invalidate_rkey = need_invalidate_rkey; + send_ctx->remote_key = remote_key; +} + +static int smb_direct_flush_send_list(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx, + bool is_last) +{ + struct smb_direct_sendmsg *first, *last; + int ret; + + if (list_empty(&send_ctx->msg_list)) + return 0; + + first = list_first_entry(&send_ctx->msg_list, + struct smb_direct_sendmsg, + list); + last = list_last_entry(&send_ctx->msg_list, + struct smb_direct_sendmsg, + list); + + last->wr.send_flags = IB_SEND_SIGNALED; + last->wr.wr_cqe = &last->cqe; + if (is_last && send_ctx->need_invalidate_rkey) { + last->wr.opcode = IB_WR_SEND_WITH_INV; + last->wr.ex.invalidate_rkey = send_ctx->remote_key; + } + + ret = smb_direct_post_send(t, &first->wr); + if (!ret) { + smb_direct_send_ctx_init(t, send_ctx, + send_ctx->need_invalidate_rkey, + send_ctx->remote_key); + } else { + atomic_add(send_ctx->wr_cnt, &t->send_credits); + wake_up(&t->wait_send_credits); + list_for_each_entry_safe(first, last, &send_ctx->msg_list, + list) { + smb_direct_free_sendmsg(t, first); + } + } + return ret; +} + +static int wait_for_credits(struct smb_direct_transport *t, + wait_queue_head_t *waitq, atomic_t *total_credits, + int needed) +{ + int ret; + + do { + if (atomic_sub_return(needed, total_credits) >= 0) + return 0; + + atomic_add(needed, total_credits); + ret = wait_event_interruptible(*waitq, + atomic_read(total_credits) >= needed || + t->status != SMB_DIRECT_CS_CONNECTED); + + if (t->status != SMB_DIRECT_CS_CONNECTED) + return -ENOTCONN; + else if (ret < 0) + return ret; + } while (true); +} + +static int wait_for_send_credits(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx) +{ + int ret; + + if (send_ctx && + (send_ctx->wr_cnt >= 16 || atomic_read(&t->send_credits) <= 1)) { + ret = smb_direct_flush_send_list(t, send_ctx, false); + if (ret) + return ret; + } + + return wait_for_credits(t, &t->wait_send_credits, &t->send_credits, 1); +} + +static int wait_for_rw_credits(struct smb_direct_transport *t, int credits) +{ + return wait_for_credits(t, &t->wait_rw_credits, &t->rw_credits, credits); +} + +static int calc_rw_credits(struct smb_direct_transport *t, + char *buf, unsigned int len) +{ + return DIV_ROUND_UP(get_buf_page_count(buf, len), + t->pages_per_rw_credit); +} + +static int smb_direct_create_header(struct smb_direct_transport *t, + int size, int remaining_data_length, + struct smb_direct_sendmsg **sendmsg_out) +{ + struct smb_direct_sendmsg *sendmsg; + struct smb_direct_data_transfer *packet; + int header_length; + int ret; + + sendmsg = smb_direct_alloc_sendmsg(t); + if (IS_ERR(sendmsg)) + return PTR_ERR(sendmsg); + + /* Fill in the packet header */ + packet = (struct smb_direct_data_transfer *)sendmsg->packet; + packet->credits_requested = cpu_to_le16(t->send_credit_target); + packet->credits_granted = cpu_to_le16(manage_credits_prior_sending(t)); + + packet->flags = 0; + packet->reserved = 0; + if (!size) + packet->data_offset = 0; + else + packet->data_offset = cpu_to_le32(24); + packet->data_length = cpu_to_le32(size); + packet->remaining_data_length = cpu_to_le32(remaining_data_length); + packet->padding = 0; + + ksmbd_debug(RDMA, + "credits_requested=%d credits_granted=%d data_offset=%d data_length=%d remaining_data_length=%d\n", + le16_to_cpu(packet->credits_requested), + le16_to_cpu(packet->credits_granted), + le32_to_cpu(packet->data_offset), + le32_to_cpu(packet->data_length), + le32_to_cpu(packet->remaining_data_length)); + + /* Map the packet to DMA */ + header_length = sizeof(struct smb_direct_data_transfer); + /* If this is a packet without payload, don't send padding */ + if (!size) + header_length = + offsetof(struct smb_direct_data_transfer, padding); + + sendmsg->sge[0].addr = ib_dma_map_single(t->cm_id->device, + (void *)packet, + header_length, + DMA_TO_DEVICE); + ret = ib_dma_mapping_error(t->cm_id->device, sendmsg->sge[0].addr); + if (ret) { + smb_direct_free_sendmsg(t, sendmsg); + return ret; + } + + sendmsg->num_sge = 1; + sendmsg->sge[0].length = header_length; + sendmsg->sge[0].lkey = t->pd->local_dma_lkey; + + *sendmsg_out = sendmsg; + return 0; +} + +static int get_sg_list(void *buf, int size, struct scatterlist *sg_list, int nentries) +{ + bool high = is_vmalloc_addr(buf); + struct page *page; + int offset, len; + int i = 0; + + if (size <= 0 || nentries < get_buf_page_count(buf, size)) + return -EINVAL; + + offset = offset_in_page(buf); + buf -= offset; + while (size > 0) { + len = min_t(int, PAGE_SIZE - offset, size); + if (high) + page = vmalloc_to_page(buf); + else + page = kmap_to_page(buf); + + if (!sg_list) + return -EINVAL; + sg_set_page(sg_list, page, len, offset); + sg_list = sg_next(sg_list); + + buf += PAGE_SIZE; + size -= len; + offset = 0; + i++; + } + return i; +} + +static int get_mapped_sg_list(struct ib_device *device, void *buf, int size, + struct scatterlist *sg_list, int nentries, + enum dma_data_direction dir) +{ + int npages; + + npages = get_sg_list(buf, size, sg_list, nentries); + if (npages < 0) + return -EINVAL; + return ib_dma_map_sg(device, sg_list, npages, dir); +} + +static int post_sendmsg(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx, + struct smb_direct_sendmsg *msg) +{ + int i; + + for (i = 0; i < msg->num_sge; i++) + ib_dma_sync_single_for_device(t->cm_id->device, + msg->sge[i].addr, msg->sge[i].length, + DMA_TO_DEVICE); + + msg->cqe.done = send_done; + msg->wr.opcode = IB_WR_SEND; + msg->wr.sg_list = &msg->sge[0]; + msg->wr.num_sge = msg->num_sge; + msg->wr.next = NULL; + + if (send_ctx) { + msg->wr.wr_cqe = NULL; + msg->wr.send_flags = 0; + if (!list_empty(&send_ctx->msg_list)) { + struct smb_direct_sendmsg *last; + + last = list_last_entry(&send_ctx->msg_list, + struct smb_direct_sendmsg, + list); + last->wr.next = &msg->wr; + } + list_add_tail(&msg->list, &send_ctx->msg_list); + send_ctx->wr_cnt++; + return 0; + } + + msg->wr.wr_cqe = &msg->cqe; + msg->wr.send_flags = IB_SEND_SIGNALED; + return smb_direct_post_send(t, &msg->wr); +} + +static int smb_direct_post_send_data(struct smb_direct_transport *t, + struct smb_direct_send_ctx *send_ctx, + struct kvec *iov, int niov, + int remaining_data_length) +{ + int i, j, ret; + struct smb_direct_sendmsg *msg; + int data_length; + struct scatterlist sg[SMB_DIRECT_MAX_SEND_SGES - 1]; + + ret = wait_for_send_credits(t, send_ctx); + if (ret) + return ret; + + data_length = 0; + for (i = 0; i < niov; i++) + data_length += iov[i].iov_len; + + ret = smb_direct_create_header(t, data_length, remaining_data_length, + &msg); + if (ret) { + atomic_inc(&t->send_credits); + return ret; + } + + for (i = 0; i < niov; i++) { + struct ib_sge *sge; + int sg_cnt; + + sg_init_table(sg, SMB_DIRECT_MAX_SEND_SGES - 1); + sg_cnt = get_mapped_sg_list(t->cm_id->device, + iov[i].iov_base, iov[i].iov_len, + sg, SMB_DIRECT_MAX_SEND_SGES - 1, + DMA_TO_DEVICE); + if (sg_cnt <= 0) { + pr_err("failed to map buffer\n"); + ret = -ENOMEM; + goto err; + } else if (sg_cnt + msg->num_sge > SMB_DIRECT_MAX_SEND_SGES) { + pr_err("buffer not fitted into sges\n"); + ret = -E2BIG; + ib_dma_unmap_sg(t->cm_id->device, sg, sg_cnt, + DMA_TO_DEVICE); + goto err; + } + + for (j = 0; j < sg_cnt; j++) { + sge = &msg->sge[msg->num_sge]; + sge->addr = sg_dma_address(&sg[j]); + sge->length = sg_dma_len(&sg[j]); + sge->lkey = t->pd->local_dma_lkey; + msg->num_sge++; + } + } + + ret = post_sendmsg(t, send_ctx, msg); + if (ret) + goto err; + return 0; +err: + smb_direct_free_sendmsg(t, msg); + atomic_inc(&t->send_credits); + return ret; +} + +static int smb_direct_writev(struct ksmbd_transport *t, + struct kvec *iov, int niovs, int buflen, + bool need_invalidate, unsigned int remote_key) +{ + struct smb_direct_transport *st = smb_trans_direct_transfort(t); + int remaining_data_length; + int start, i, j; + int max_iov_size = st->max_send_size - + sizeof(struct smb_direct_data_transfer); + int ret; + struct kvec vec; + struct smb_direct_send_ctx send_ctx; + + if (st->status != SMB_DIRECT_CS_CONNECTED) + return -ENOTCONN; + + //FIXME: skip RFC1002 header.. + buflen -= 4; + iov[0].iov_base += 4; + iov[0].iov_len -= 4; + + remaining_data_length = buflen; + ksmbd_debug(RDMA, "Sending smb (RDMA): smb_len=%u\n", buflen); + + smb_direct_send_ctx_init(st, &send_ctx, need_invalidate, remote_key); + start = i = 0; + buflen = 0; + while (true) { + buflen += iov[i].iov_len; + if (buflen > max_iov_size) { + if (i > start) { + remaining_data_length -= + (buflen - iov[i].iov_len); + ret = smb_direct_post_send_data(st, &send_ctx, + &iov[start], i - start, + remaining_data_length); + if (ret) + goto done; + } else { + /* iov[start] is too big, break it */ + int nvec = (buflen + max_iov_size - 1) / + max_iov_size; + + for (j = 0; j < nvec; j++) { + vec.iov_base = + (char *)iov[start].iov_base + + j * max_iov_size; + vec.iov_len = + min_t(int, max_iov_size, + buflen - max_iov_size * j); + remaining_data_length -= vec.iov_len; + ret = smb_direct_post_send_data(st, &send_ctx, &vec, 1, + remaining_data_length); + if (ret) + goto done; + } + i++; + if (i == niovs) + break; + } + start = i; + buflen = 0; + } else { + i++; + if (i == niovs) { + /* send out all remaining vecs */ + remaining_data_length -= buflen; + ret = smb_direct_post_send_data(st, &send_ctx, + &iov[start], i - start, + remaining_data_length); + if (ret) + goto done; + break; + } + } + } + +done: + ret = smb_direct_flush_send_list(st, &send_ctx, true); + + /* + * As an optimization, we don't wait for individual I/O to finish + * before sending the next one. + * Send them all and wait for pending send count to get to 0 + * that means all the I/Os have been out and we are good to return + */ + + wait_event(st->wait_send_pending, + atomic_read(&st->send_pending) == 0); + return ret; +} + +static void smb_direct_free_rdma_rw_msg(struct smb_direct_transport *t, + struct smb_direct_rdma_rw_msg *msg, + enum dma_data_direction dir) +{ + rdma_rw_ctx_destroy(&msg->rw_ctx, t->qp, t->qp->port, + msg->sgt.sgl, msg->sgt.nents, dir); + sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); + kfree(msg); +} + +static void read_write_done(struct ib_cq *cq, struct ib_wc *wc, + enum dma_data_direction dir) +{ + struct smb_direct_rdma_rw_msg *msg = container_of(wc->wr_cqe, + struct smb_direct_rdma_rw_msg, cqe); + struct smb_direct_transport *t = msg->t; + + if (wc->status != IB_WC_SUCCESS) { + msg->status = -EIO; + pr_err("read/write error. opcode = %d, status = %s(%d)\n", + wc->opcode, ib_wc_status_msg(wc->status), wc->status); + if (wc->status != IB_WC_WR_FLUSH_ERR) + smb_direct_disconnect_rdma_connection(t); + } + + complete(msg->completion); +} + +static void read_done(struct ib_cq *cq, struct ib_wc *wc) +{ + read_write_done(cq, wc, DMA_FROM_DEVICE); +} + +static void write_done(struct ib_cq *cq, struct ib_wc *wc) +{ + read_write_done(cq, wc, DMA_TO_DEVICE); +} + +static int smb_direct_rdma_xmit(struct smb_direct_transport *t, + void *buf, int buf_len, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len, + bool is_read) +{ + struct smb_direct_rdma_rw_msg *msg, *next_msg; + int i, ret; + DECLARE_COMPLETION_ONSTACK(completion); + struct ib_send_wr *first_wr; + LIST_HEAD(msg_list); + char *desc_buf; + int credits_needed; + unsigned int desc_buf_len; + size_t total_length = 0; + + if (t->status != SMB_DIRECT_CS_CONNECTED) + return -ENOTCONN; + + /* calculate needed credits */ + credits_needed = 0; + desc_buf = buf; + for (i = 0; i < desc_len / sizeof(*desc); i++) { + desc_buf_len = le32_to_cpu(desc[i].length); + + credits_needed += calc_rw_credits(t, desc_buf, desc_buf_len); + desc_buf += desc_buf_len; + total_length += desc_buf_len; + if (desc_buf_len == 0 || total_length > buf_len || + total_length > t->max_rdma_rw_size) + return -EINVAL; + } + + ksmbd_debug(RDMA, "RDMA %s, len %#x, needed credits %#x\n", + is_read ? "read" : "write", buf_len, credits_needed); + + ret = wait_for_rw_credits(t, credits_needed); + if (ret < 0) + return ret; + + /* build rdma_rw_ctx for each descriptor */ + desc_buf = buf; + for (i = 0; i < desc_len / sizeof(*desc); i++) { + msg = kzalloc(offsetof(struct smb_direct_rdma_rw_msg, sg_list) + + sizeof(struct scatterlist) * SG_CHUNK_SIZE, GFP_KERNEL); + if (!msg) { + ret = -ENOMEM; + goto out; + } + + desc_buf_len = le32_to_cpu(desc[i].length); + + msg->t = t; + msg->cqe.done = is_read ? read_done : write_done; + msg->completion = &completion; + + msg->sgt.sgl = &msg->sg_list[0]; + ret = sg_alloc_table_chained(&msg->sgt, + get_buf_page_count(desc_buf, desc_buf_len), + msg->sg_list, SG_CHUNK_SIZE); + if (ret) { + kfree(msg); + ret = -ENOMEM; + goto out; + } + + ret = get_sg_list(desc_buf, desc_buf_len, + msg->sgt.sgl, msg->sgt.orig_nents); + if (ret < 0) { + sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); + kfree(msg); + goto out; + } + + ret = rdma_rw_ctx_init(&msg->rw_ctx, t->qp, t->qp->port, + msg->sgt.sgl, + get_buf_page_count(desc_buf, desc_buf_len), + 0, + le64_to_cpu(desc[i].offset), + le32_to_cpu(desc[i].token), + is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (ret < 0) { + pr_err("failed to init rdma_rw_ctx: %d\n", ret); + sg_free_table_chained(&msg->sgt, SG_CHUNK_SIZE); + kfree(msg); + goto out; + } + + list_add_tail(&msg->list, &msg_list); + desc_buf += desc_buf_len; + } + + /* concatenate work requests of rdma_rw_ctxs */ + first_wr = NULL; + list_for_each_entry_reverse(msg, &msg_list, list) { + first_wr = rdma_rw_ctx_wrs(&msg->rw_ctx, t->qp, t->qp->port, + &msg->cqe, first_wr); + } + + ret = ib_post_send(t->qp, first_wr, NULL); + if (ret) { + pr_err("failed to post send wr for RDMA R/W: %d\n", ret); + goto out; + } + + msg = list_last_entry(&msg_list, struct smb_direct_rdma_rw_msg, list); + wait_for_completion(&completion); + ret = msg->status; +out: + list_for_each_entry_safe(msg, next_msg, &msg_list, list) { + list_del(&msg->list); + smb_direct_free_rdma_rw_msg(t, msg, + is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + } + atomic_add(credits_needed, &t->rw_credits); + wake_up(&t->wait_rw_credits); + return ret; +} + +static int smb_direct_rdma_write(struct ksmbd_transport *t, + void *buf, unsigned int buflen, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len) +{ + return smb_direct_rdma_xmit(smb_trans_direct_transfort(t), buf, buflen, + desc, desc_len, false); +} + +static int smb_direct_rdma_read(struct ksmbd_transport *t, + void *buf, unsigned int buflen, + struct smb2_buffer_desc_v1 *desc, + unsigned int desc_len) +{ + return smb_direct_rdma_xmit(smb_trans_direct_transfort(t), buf, buflen, + desc, desc_len, true); +} + +static void smb_direct_disconnect(struct ksmbd_transport *t) +{ + struct smb_direct_transport *st = smb_trans_direct_transfort(t); + + ksmbd_debug(RDMA, "Disconnecting cm_id=%p\n", st->cm_id); + + smb_direct_disconnect_rdma_work(&st->disconnect_work); + wait_event_interruptible(st->wait_status, + st->status == SMB_DIRECT_CS_DISCONNECTED); + free_transport(st); +} + +static void smb_direct_shutdown(struct ksmbd_transport *t) +{ + struct smb_direct_transport *st = smb_trans_direct_transfort(t); + + ksmbd_debug(RDMA, "smb-direct shutdown cm_id=%p\n", st->cm_id); + + smb_direct_disconnect_rdma_work(&st->disconnect_work); +} + +static int smb_direct_cm_handler(struct rdma_cm_id *cm_id, + struct rdma_cm_event *event) +{ + struct smb_direct_transport *t = cm_id->context; + + ksmbd_debug(RDMA, "RDMA CM event. cm_id=%p event=%s (%d)\n", + cm_id, rdma_event_msg(event->event), event->event); + + switch (event->event) { + case RDMA_CM_EVENT_ESTABLISHED: { + t->status = SMB_DIRECT_CS_CONNECTED; + wake_up_interruptible(&t->wait_status); + break; + } + case RDMA_CM_EVENT_DEVICE_REMOVAL: + case RDMA_CM_EVENT_DISCONNECTED: { + ib_drain_qp(t->qp); + + t->status = SMB_DIRECT_CS_DISCONNECTED; + wake_up_interruptible(&t->wait_status); + wake_up_interruptible(&t->wait_reassembly_queue); + wake_up(&t->wait_send_credits); + break; + } + case RDMA_CM_EVENT_CONNECT_ERROR: { + t->status = SMB_DIRECT_CS_DISCONNECTED; + wake_up_interruptible(&t->wait_status); + break; + } + default: + pr_err("Unexpected RDMA CM event. cm_id=%p, event=%s (%d)\n", + cm_id, rdma_event_msg(event->event), + event->event); + break; + } + return 0; +} + +static void smb_direct_qpair_handler(struct ib_event *event, void *context) +{ + struct smb_direct_transport *t = context; + + ksmbd_debug(RDMA, "Received QP event. cm_id=%p, event=%s (%d)\n", + t->cm_id, ib_event_msg(event->event), event->event); + + switch (event->event) { + case IB_EVENT_CQ_ERR: + case IB_EVENT_QP_FATAL: + smb_direct_disconnect_rdma_connection(t); + break; + default: + break; + } +} + +static int smb_direct_send_negotiate_response(struct smb_direct_transport *t, + int failed) +{ + struct smb_direct_sendmsg *sendmsg; + struct smb_direct_negotiate_resp *resp; + int ret; + + sendmsg = smb_direct_alloc_sendmsg(t); + if (IS_ERR(sendmsg)) + return -ENOMEM; + + resp = (struct smb_direct_negotiate_resp *)sendmsg->packet; + if (failed) { + memset(resp, 0, sizeof(*resp)); + resp->min_version = cpu_to_le16(0x0100); + resp->max_version = cpu_to_le16(0x0100); + resp->status = STATUS_NOT_SUPPORTED; + } else { + resp->status = STATUS_SUCCESS; + resp->min_version = SMB_DIRECT_VERSION_LE; + resp->max_version = SMB_DIRECT_VERSION_LE; + resp->negotiated_version = SMB_DIRECT_VERSION_LE; + resp->reserved = 0; + resp->credits_requested = + cpu_to_le16(t->send_credit_target); + resp->credits_granted = cpu_to_le16(manage_credits_prior_sending(t)); + resp->max_readwrite_size = cpu_to_le32(t->max_rdma_rw_size); + resp->preferred_send_size = cpu_to_le32(t->max_send_size); + resp->max_receive_size = cpu_to_le32(t->max_recv_size); + resp->max_fragmented_size = + cpu_to_le32(t->max_fragmented_recv_size); + } + + sendmsg->sge[0].addr = ib_dma_map_single(t->cm_id->device, + (void *)resp, sizeof(*resp), + DMA_TO_DEVICE); + ret = ib_dma_mapping_error(t->cm_id->device, sendmsg->sge[0].addr); + if (ret) { + smb_direct_free_sendmsg(t, sendmsg); + return ret; + } + + sendmsg->num_sge = 1; + sendmsg->sge[0].length = sizeof(*resp); + sendmsg->sge[0].lkey = t->pd->local_dma_lkey; + + ret = post_sendmsg(t, NULL, sendmsg); + if (ret) { + smb_direct_free_sendmsg(t, sendmsg); + return ret; + } + + wait_event(t->wait_send_pending, + atomic_read(&t->send_pending) == 0); + return 0; +} + +static int smb_direct_accept_client(struct smb_direct_transport *t) +{ + struct rdma_conn_param conn_param; + struct ib_port_immutable port_immutable; + u32 ird_ord_hdr[2]; + int ret; + + memset(&conn_param, 0, sizeof(conn_param)); + conn_param.initiator_depth = min_t(u8, t->cm_id->device->attrs.max_qp_rd_atom, + SMB_DIRECT_CM_INITIATOR_DEPTH); + conn_param.responder_resources = 0; + + t->cm_id->device->ops.get_port_immutable(t->cm_id->device, + t->cm_id->port_num, + &port_immutable); + if (port_immutable.core_cap_flags & RDMA_CORE_PORT_IWARP) { + ird_ord_hdr[0] = conn_param.responder_resources; + ird_ord_hdr[1] = 1; + conn_param.private_data = ird_ord_hdr; + conn_param.private_data_len = sizeof(ird_ord_hdr); + } else { + conn_param.private_data = NULL; + conn_param.private_data_len = 0; + } + conn_param.retry_count = SMB_DIRECT_CM_RETRY; + conn_param.rnr_retry_count = SMB_DIRECT_CM_RNR_RETRY; + conn_param.flow_control = 0; + + ret = rdma_accept(t->cm_id, &conn_param); + if (ret) { + pr_err("error at rdma_accept: %d\n", ret); + return ret; + } + return 0; +} + +static int smb_direct_prepare_negotiation(struct smb_direct_transport *t) +{ + int ret; + struct smb_direct_recvmsg *recvmsg; + + recvmsg = get_free_recvmsg(t); + if (!recvmsg) + return -ENOMEM; + recvmsg->type = SMB_DIRECT_MSG_NEGOTIATE_REQ; + + ret = smb_direct_post_recv(t, recvmsg); + if (ret) { + pr_err("Can't post recv: %d\n", ret); + goto out_err; + } + + t->negotiation_requested = false; + ret = smb_direct_accept_client(t); + if (ret) { + pr_err("Can't accept client\n"); + goto out_err; + } + + smb_direct_post_recv_credits(&t->post_recv_credits_work.work); + return 0; +out_err: + put_recvmsg(t, recvmsg); + return ret; +} + +static unsigned int smb_direct_get_max_fr_pages(struct smb_direct_transport *t) +{ + return min_t(unsigned int, + t->cm_id->device->attrs.max_fast_reg_page_list_len, + 256); +} + +static int smb_direct_init_params(struct smb_direct_transport *t, + struct ib_qp_cap *cap) +{ + struct ib_device *device = t->cm_id->device; + int max_send_sges, max_rw_wrs, max_send_wrs; + unsigned int max_sge_per_wr, wrs_per_credit; + + /* need 2 more sge. because a SMB_DIRECT header will be mapped, + * and maybe a send buffer could be not page aligned. + */ + t->max_send_size = smb_direct_max_send_size; + max_send_sges = DIV_ROUND_UP(t->max_send_size, PAGE_SIZE) + 2; + if (max_send_sges > SMB_DIRECT_MAX_SEND_SGES) { + pr_err("max_send_size %d is too large\n", t->max_send_size); + return -EINVAL; + } + + /* Calculate the number of work requests for RDMA R/W. + * The maximum number of pages which can be registered + * with one Memory region can be transferred with one + * R/W credit. And at least 4 work requests for each credit + * are needed for MR registration, RDMA R/W, local & remote + * MR invalidation. + */ + t->max_rdma_rw_size = smb_direct_max_read_write_size; + t->pages_per_rw_credit = smb_direct_get_max_fr_pages(t); + t->max_rw_credits = DIV_ROUND_UP(t->max_rdma_rw_size, + (t->pages_per_rw_credit - 1) * + PAGE_SIZE); + + max_sge_per_wr = min_t(unsigned int, device->attrs.max_send_sge, + device->attrs.max_sge_rd); + wrs_per_credit = max_t(unsigned int, 4, + DIV_ROUND_UP(t->pages_per_rw_credit, + max_sge_per_wr) + 1); + max_rw_wrs = t->max_rw_credits * wrs_per_credit; + + max_send_wrs = smb_direct_send_credit_target + max_rw_wrs; + if (max_send_wrs > device->attrs.max_cqe || + max_send_wrs > device->attrs.max_qp_wr) { + pr_err("consider lowering send_credit_target = %d\n", + smb_direct_send_credit_target); + pr_err("Possible CQE overrun, device reporting max_cqe %d max_qp_wr %d\n", + device->attrs.max_cqe, device->attrs.max_qp_wr); + return -EINVAL; + } + + if (smb_direct_receive_credit_max > device->attrs.max_cqe || + smb_direct_receive_credit_max > device->attrs.max_qp_wr) { + pr_err("consider lowering receive_credit_max = %d\n", + smb_direct_receive_credit_max); + pr_err("Possible CQE overrun, device reporting max_cpe %d max_qp_wr %d\n", + device->attrs.max_cqe, device->attrs.max_qp_wr); + return -EINVAL; + } + + if (device->attrs.max_send_sge < SMB_DIRECT_MAX_SEND_SGES) { + pr_err("warning: device max_send_sge = %d too small\n", + device->attrs.max_send_sge); + return -EINVAL; + } + if (device->attrs.max_recv_sge < SMB_DIRECT_MAX_RECV_SGES) { + pr_err("warning: device max_recv_sge = %d too small\n", + device->attrs.max_recv_sge); + return -EINVAL; + } + + t->recv_credits = 0; + t->count_avail_recvmsg = 0; + + t->recv_credit_max = smb_direct_receive_credit_max; + t->recv_credit_target = 10; + t->new_recv_credits = 0; + + t->send_credit_target = smb_direct_send_credit_target; + atomic_set(&t->send_credits, 0); + atomic_set(&t->rw_credits, t->max_rw_credits); + + t->max_send_size = smb_direct_max_send_size; + t->max_recv_size = smb_direct_max_receive_size; + t->max_fragmented_recv_size = smb_direct_max_fragmented_recv_size; + + cap->max_send_wr = max_send_wrs; + cap->max_recv_wr = t->recv_credit_max; + cap->max_send_sge = max_sge_per_wr; + cap->max_recv_sge = SMB_DIRECT_MAX_RECV_SGES; + cap->max_inline_data = 0; + cap->max_rdma_ctxs = t->max_rw_credits; + return 0; +} + +static void smb_direct_destroy_pools(struct smb_direct_transport *t) +{ + struct smb_direct_recvmsg *recvmsg; + + while ((recvmsg = get_free_recvmsg(t))) + mempool_free(recvmsg, t->recvmsg_mempool); + while ((recvmsg = get_empty_recvmsg(t))) + mempool_free(recvmsg, t->recvmsg_mempool); + + mempool_destroy(t->recvmsg_mempool); + t->recvmsg_mempool = NULL; + + kmem_cache_destroy(t->recvmsg_cache); + t->recvmsg_cache = NULL; + + mempool_destroy(t->sendmsg_mempool); + t->sendmsg_mempool = NULL; + + kmem_cache_destroy(t->sendmsg_cache); + t->sendmsg_cache = NULL; +} + +static int smb_direct_create_pools(struct smb_direct_transport *t) +{ + char name[80]; + int i; + struct smb_direct_recvmsg *recvmsg; + + snprintf(name, sizeof(name), "smb_direct_rqst_pool_%p", t); + t->sendmsg_cache = kmem_cache_create(name, + sizeof(struct smb_direct_sendmsg) + + sizeof(struct smb_direct_negotiate_resp), + 0, SLAB_HWCACHE_ALIGN, NULL); + if (!t->sendmsg_cache) + return -ENOMEM; + + t->sendmsg_mempool = mempool_create(t->send_credit_target, + mempool_alloc_slab, mempool_free_slab, + t->sendmsg_cache); + if (!t->sendmsg_mempool) + goto err; + + snprintf(name, sizeof(name), "smb_direct_resp_%p", t); + t->recvmsg_cache = kmem_cache_create(name, + sizeof(struct smb_direct_recvmsg) + + t->max_recv_size, + 0, SLAB_HWCACHE_ALIGN, NULL); + if (!t->recvmsg_cache) + goto err; + + t->recvmsg_mempool = + mempool_create(t->recv_credit_max, mempool_alloc_slab, + mempool_free_slab, t->recvmsg_cache); + if (!t->recvmsg_mempool) + goto err; + + INIT_LIST_HEAD(&t->recvmsg_queue); + + for (i = 0; i < t->recv_credit_max; i++) { + recvmsg = mempool_alloc(t->recvmsg_mempool, GFP_KERNEL); + if (!recvmsg) + goto err; + recvmsg->transport = t; + list_add(&recvmsg->list, &t->recvmsg_queue); + } + t->count_avail_recvmsg = t->recv_credit_max; + + return 0; +err: + smb_direct_destroy_pools(t); + return -ENOMEM; +} + +static int smb_direct_create_qpair(struct smb_direct_transport *t, + struct ib_qp_cap *cap) +{ + int ret; + struct ib_qp_init_attr qp_attr; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0) + int pages_per_rw; +#endif + + t->pd = ib_alloc_pd(t->cm_id->device, 0); + if (IS_ERR(t->pd)) { + pr_err("Can't create RDMA PD\n"); + ret = PTR_ERR(t->pd); + t->pd = NULL; + return ret; + } + + t->send_cq = ib_alloc_cq(t->cm_id->device, t, + smb_direct_send_credit_target + cap->max_rdma_ctxs, + 0, IB_POLL_WORKQUEUE); + if (IS_ERR(t->send_cq)) { + pr_err("Can't create RDMA send CQ\n"); + ret = PTR_ERR(t->send_cq); + t->send_cq = NULL; + goto err; + } + + t->recv_cq = ib_alloc_cq(t->cm_id->device, t, + t->recv_credit_max, 0, IB_POLL_WORKQUEUE); + if (IS_ERR(t->recv_cq)) { + pr_err("Can't create RDMA recv CQ\n"); + ret = PTR_ERR(t->recv_cq); + t->recv_cq = NULL; + goto err; + } + + memset(&qp_attr, 0, sizeof(qp_attr)); + qp_attr.event_handler = smb_direct_qpair_handler; + qp_attr.qp_context = t; + qp_attr.cap = *cap; + qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR; + qp_attr.qp_type = IB_QPT_RC; + qp_attr.send_cq = t->send_cq; + qp_attr.recv_cq = t->recv_cq; + qp_attr.port_num = ~0; + + ret = rdma_create_qp(t->cm_id, t->pd, &qp_attr); + if (ret) { + pr_err("Can't create RDMA QP: %d\n", ret); + goto err; + } + + t->qp = t->cm_id->qp; + t->cm_id->event_handler = smb_direct_cm_handler; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0) + pages_per_rw = DIV_ROUND_UP(t->max_rdma_rw_size, PAGE_SIZE) + 1; + if (pages_per_rw > t->cm_id->device->attrs.max_sgl_rd) { +#endif + ret = ib_mr_pool_init(t->qp, &t->qp->rdma_mrs, + t->max_rw_credits, IB_MR_TYPE_MEM_REG, + t->pages_per_rw_credit, 0); + if (ret) { + pr_err("failed to init mr pool count %d pages %d\n", + t->max_rw_credits, t->pages_per_rw_credit); + goto err; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0) + } +#endif + return 0; +err: + if (t->qp) { + ib_destroy_qp(t->qp); + t->qp = NULL; + } + if (t->recv_cq) { + ib_destroy_cq(t->recv_cq); + t->recv_cq = NULL; + } + if (t->send_cq) { + ib_destroy_cq(t->send_cq); + t->send_cq = NULL; + } + if (t->pd) { + ib_dealloc_pd(t->pd); + t->pd = NULL; + } + return ret; +} + +static int smb_direct_prepare(struct ksmbd_transport *t) +{ + struct smb_direct_transport *st = smb_trans_direct_transfort(t); + struct smb_direct_recvmsg *recvmsg; + struct smb_direct_negotiate_req *req; + int ret; + + ksmbd_debug(RDMA, "Waiting for SMB_DIRECT negotiate request\n"); + ret = wait_event_interruptible_timeout(st->wait_status, + st->negotiation_requested || + st->status == SMB_DIRECT_CS_DISCONNECTED, + SMB_DIRECT_NEGOTIATE_TIMEOUT * HZ); + if (ret <= 0 || st->status == SMB_DIRECT_CS_DISCONNECTED) + return ret < 0 ? ret : -ETIMEDOUT; + + recvmsg = get_first_reassembly(st); + if (!recvmsg) + return -ECONNABORTED; + + ret = smb_direct_check_recvmsg(recvmsg); + if (ret == -ECONNABORTED) + goto out; + + req = (struct smb_direct_negotiate_req *)recvmsg->packet; + st->max_recv_size = min_t(int, st->max_recv_size, + le32_to_cpu(req->preferred_send_size)); + st->max_send_size = min_t(int, st->max_send_size, + le32_to_cpu(req->max_receive_size)); + st->max_fragmented_send_size = + le32_to_cpu(req->max_fragmented_size); + st->max_fragmented_recv_size = + (st->recv_credit_max * st->max_recv_size) / 2; + + ret = smb_direct_send_negotiate_response(st, ret); +out: + spin_lock_irq(&st->reassembly_queue_lock); + st->reassembly_queue_length--; + list_del(&recvmsg->list); + spin_unlock_irq(&st->reassembly_queue_lock); + put_recvmsg(st, recvmsg); + + return ret; +} + +static int smb_direct_connect(struct smb_direct_transport *st) +{ + int ret; + struct ib_qp_cap qp_cap; + + ret = smb_direct_init_params(st, &qp_cap); + if (ret) { + pr_err("Can't configure RDMA parameters\n"); + return ret; + } + + ret = smb_direct_create_pools(st); + if (ret) { + pr_err("Can't init RDMA pool: %d\n", ret); + return ret; + } + + ret = smb_direct_create_qpair(st, &qp_cap); + if (ret) { + pr_err("Can't accept RDMA client: %d\n", ret); + return ret; + } + + ret = smb_direct_prepare_negotiation(st); + if (ret) { + pr_err("Can't negotiate: %d\n", ret); + return ret; + } + return 0; +} + +static bool rdma_frwr_is_supported(struct ib_device_attr *attrs) +{ + if (!(attrs->device_cap_flags & IB_DEVICE_MEM_MGT_EXTENSIONS)) + return false; + if (attrs->max_fast_reg_page_list_len == 0) + return false; + return true; +} + +static int smb_direct_handle_connect_request(struct rdma_cm_id *new_cm_id) +{ + struct smb_direct_transport *t; + int ret; + + if (!rdma_frwr_is_supported(&new_cm_id->device->attrs)) { + ksmbd_debug(RDMA, + "Fast Registration Work Requests is not supported. device capabilities=%llx\n", + new_cm_id->device->attrs.device_cap_flags); + return -EPROTONOSUPPORT; + } + + t = alloc_transport(new_cm_id); + if (!t) + return -ENOMEM; + + ret = smb_direct_connect(t); + if (ret) + goto out_err; + + KSMBD_TRANS(t)->handler = kthread_run(ksmbd_conn_handler_loop, + KSMBD_TRANS(t)->conn, "ksmbd:r%u", + smb_direct_port); + if (IS_ERR(KSMBD_TRANS(t)->handler)) { + ret = PTR_ERR(KSMBD_TRANS(t)->handler); + pr_err("Can't start thread\n"); + goto out_err; + } + + return 0; +out_err: + free_transport(t); + return ret; +} + +static int smb_direct_listen_handler(struct rdma_cm_id *cm_id, + struct rdma_cm_event *event) +{ + switch (event->event) { + case RDMA_CM_EVENT_CONNECT_REQUEST: { + int ret = smb_direct_handle_connect_request(cm_id); + + if (ret) { + pr_err("Can't create transport: %d\n", ret); + return ret; + } + + ksmbd_debug(RDMA, "Received connection request. cm_id=%p\n", + cm_id); + break; + } + default: + pr_err("Unexpected listen event. cm_id=%p, event=%s (%d)\n", + cm_id, rdma_event_msg(event->event), event->event); + break; + } + return 0; +} + +static int smb_direct_listen(int port) +{ + int ret; + struct rdma_cm_id *cm_id; + struct sockaddr_in sin = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(INADDR_ANY), + .sin_port = htons(port), + }; + + cm_id = rdma_create_id(&init_net, smb_direct_listen_handler, + &smb_direct_listener, RDMA_PS_TCP, IB_QPT_RC); + if (IS_ERR(cm_id)) { + pr_err("Can't create cm id: %ld\n", PTR_ERR(cm_id)); + return PTR_ERR(cm_id); + } + + ret = rdma_bind_addr(cm_id, (struct sockaddr *)&sin); + if (ret) { + pr_err("Can't bind: %d\n", ret); + goto err; + } + + smb_direct_listener.cm_id = cm_id; + + ret = rdma_listen(cm_id, 10); + if (ret) { + pr_err("Can't listen: %d\n", ret); + goto err; + } + return 0; +err: + smb_direct_listener.cm_id = NULL; + rdma_destroy_id(cm_id); + return ret; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) +static int smb_direct_ib_client_add(struct ib_device *ib_dev) +#else +static void smb_direct_ib_client_add(struct ib_device *ib_dev) +#endif +{ + struct smb_direct_device *smb_dev; + + /* Set 5445 port if device type is iWARP(No IB) */ + if (ib_dev->node_type != RDMA_NODE_IB_CA) + smb_direct_port = SMB_DIRECT_PORT_IWARP; + + if (!ib_dev->ops.get_netdev || + !rdma_frwr_is_supported(&ib_dev->attrs)) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + return 0; +#else + return; +#endif + + smb_dev = kzalloc(sizeof(*smb_dev), GFP_KERNEL); + if (!smb_dev) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + return -ENOMEM; +#else + return; +#endif + smb_dev->ib_dev = ib_dev; + + write_lock(&smb_direct_device_lock); + list_add(&smb_dev->list, &smb_direct_device_list); + write_unlock(&smb_direct_device_lock); + + ksmbd_debug(RDMA, "ib device added: name %s\n", ib_dev->name); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + return 0; +#else + return; +#endif +} + +static void smb_direct_ib_client_remove(struct ib_device *ib_dev, + void *client_data) +{ + struct smb_direct_device *smb_dev, *tmp; + + write_lock(&smb_direct_device_lock); + list_for_each_entry_safe(smb_dev, tmp, &smb_direct_device_list, list) { + if (smb_dev->ib_dev == ib_dev) { + list_del(&smb_dev->list); + kfree(smb_dev); + break; + } + } + write_unlock(&smb_direct_device_lock); +} + +static struct ib_client smb_direct_ib_client = { + .name = "ksmbd_smb_direct_ib", + .add = smb_direct_ib_client_add, + .remove = smb_direct_ib_client_remove, +}; + +int ksmbd_rdma_init(void) +{ + int ret; + + smb_direct_listener.cm_id = NULL; + + ret = ib_register_client(&smb_direct_ib_client); + if (ret) { + pr_err("failed to ib_register_client\n"); + return ret; + } + + /* When a client is running out of send credits, the credits are + * granted by the server's sending a packet using this queue. + * This avoids the situation that a clients cannot send packets + * for lack of credits + */ + smb_direct_wq = alloc_workqueue("ksmbd-smb_direct-wq", + WQ_HIGHPRI | WQ_MEM_RECLAIM, 0); + if (!smb_direct_wq) + return -ENOMEM; + + ret = smb_direct_listen(smb_direct_port); + if (ret) { + destroy_workqueue(smb_direct_wq); + smb_direct_wq = NULL; + pr_err("Can't listen: %d\n", ret); + return ret; + } + + ksmbd_debug(RDMA, "init RDMA listener. cm_id=%p\n", + smb_direct_listener.cm_id); + return 0; +} + +void ksmbd_rdma_destroy(void) +{ + if (!smb_direct_listener.cm_id) + return; + + ib_unregister_client(&smb_direct_ib_client); + rdma_destroy_id(smb_direct_listener.cm_id); + + smb_direct_listener.cm_id = NULL; + + if (smb_direct_wq) { + destroy_workqueue(smb_direct_wq); + smb_direct_wq = NULL; + } +} + +bool ksmbd_rdma_capable_netdev(struct net_device *netdev) +{ + struct smb_direct_device *smb_dev; + int i; + bool rdma_capable = false; + + read_lock(&smb_direct_device_lock); + list_for_each_entry(smb_dev, &smb_direct_device_list, list) { + for (i = 0; i < smb_dev->ib_dev->phys_port_cnt; i++) { + struct net_device *ndev; + + ndev = smb_dev->ib_dev->ops.get_netdev(smb_dev->ib_dev, + i + 1); + if (!ndev) + continue; + + if (ndev == netdev) { + dev_put(ndev); + rdma_capable = true; + goto out; + } + dev_put(ndev); + } + } +out: + read_unlock(&smb_direct_device_lock); + + if (rdma_capable == false) { + struct ib_device *ibdev; + + ibdev = ib_device_get_by_netdev(netdev, RDMA_DRIVER_UNKNOWN); + if (ibdev) { + if (rdma_frwr_is_supported(&ibdev->attrs)) + rdma_capable = true; + ib_device_put(ibdev); + } + } + + return rdma_capable; +} + +static struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops = { + .prepare = smb_direct_prepare, + .disconnect = smb_direct_disconnect, + .shutdown = smb_direct_shutdown, + .writev = smb_direct_writev, + .read = smb_direct_read, + .rdma_read = smb_direct_rdma_read, + .rdma_write = smb_direct_rdma_write, +}; diff --git a/fs/ksmbd/transport_rdma.h b/fs/ksmbd/transport_rdma.h new file mode 100644 index 0000000000000..77aee4e5c9dcd --- /dev/null +++ b/fs/ksmbd/transport_rdma.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2017, Microsoft Corporation. + * Copyright (C) 2018, LG Electronics. + */ + +#ifndef __KSMBD_TRANSPORT_RDMA_H__ +#define __KSMBD_TRANSPORT_RDMA_H__ + +#define SMBD_DEFAULT_IOSIZE (8 * 1024 * 1024) +#define SMBD_MIN_IOSIZE (512 * 1024) +#define SMBD_MAX_IOSIZE (16 * 1024 * 1024) + +/* SMB DIRECT negotiation request packet [MS-SMBD] 2.2.1 */ +struct smb_direct_negotiate_req { + __le16 min_version; + __le16 max_version; + __le16 reserved; + __le16 credits_requested; + __le32 preferred_send_size; + __le32 max_receive_size; + __le32 max_fragmented_size; +} __packed; + +/* SMB DIRECT negotiation response packet [MS-SMBD] 2.2.2 */ +struct smb_direct_negotiate_resp { + __le16 min_version; + __le16 max_version; + __le16 negotiated_version; + __le16 reserved; + __le16 credits_requested; + __le16 credits_granted; + __le32 status; + __le32 max_readwrite_size; + __le32 preferred_send_size; + __le32 max_receive_size; + __le32 max_fragmented_size; +} __packed; + +#define SMB_DIRECT_RESPONSE_REQUESTED 0x0001 + +/* SMB DIRECT data transfer packet with payload [MS-SMBD] 2.2.3 */ +struct smb_direct_data_transfer { + __le16 credits_requested; + __le16 credits_granted; + __le16 flags; + __le16 reserved; + __le32 remaining_data_length; + __le32 data_offset; + __le32 data_length; + __le32 padding; + __u8 buffer[]; +} __packed; + +#ifdef CONFIG_SMB_SERVER_SMBDIRECT +int ksmbd_rdma_init(void); +void ksmbd_rdma_destroy(void); +bool ksmbd_rdma_capable_netdev(struct net_device *netdev); +void init_smbd_max_io_size(unsigned int sz); +unsigned int get_smbd_max_read_write_size(void); +#else +static inline int ksmbd_rdma_init(void) { return 0; } +static inline int ksmbd_rdma_destroy(void) { return 0; } +static inline bool ksmbd_rdma_capable_netdev(struct net_device *netdev) { return false; } +static inline void init_smbd_max_io_size(unsigned int sz) { } +static inline unsigned int get_smbd_max_read_write_size(void) { return 0; } +#endif + +#endif /* __KSMBD_TRANSPORT_RDMA_H__ */ diff --git a/fs/ksmbd/transport_tcp.c b/fs/ksmbd/transport_tcp.c new file mode 100644 index 0000000000000..a82b6c2d9cd4c --- /dev/null +++ b/fs/ksmbd/transport_tcp.c @@ -0,0 +1,678 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include + +#include "smb_common.h" +#include "server.h" +#include "auth.h" +#include "connection.h" +#include "transport_tcp.h" + +#define IFACE_STATE_DOWN BIT(0) +#define IFACE_STATE_CONFIGURED BIT(1) + +static atomic_t active_num_conn; + +struct interface { + struct task_struct *ksmbd_kthread; + struct socket *ksmbd_socket; + struct list_head entry; + char *name; + struct mutex sock_release_lock; + int state; +}; + +static LIST_HEAD(iface_list); + +static int bind_additional_ifaces; + +struct tcp_transport { + struct ksmbd_transport transport; + struct socket *sock; + struct kvec *iov; + unsigned int nr_iov; +}; + +static struct ksmbd_transport_ops ksmbd_tcp_transport_ops; + +static void tcp_stop_kthread(struct task_struct *kthread); +static struct interface *alloc_iface(char *ifname); + +#define KSMBD_TRANS(t) (&(t)->transport) +#define TCP_TRANS(t) ((struct tcp_transport *)container_of(t, \ + struct tcp_transport, transport)) + +static inline void ksmbd_tcp_nodelay(struct socket *sock) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0) + int val = 1; + + kernel_setsockopt(sock, SOL_TCP, TCP_NODELAY, (char *)&val, + sizeof(val)); +#else + tcp_sock_set_nodelay(sock->sk); +#endif +} + +static inline void ksmbd_tcp_reuseaddr(struct socket *sock) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0) + int val = 1; + + kernel_setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&val, + sizeof(val)); +#else + sock_set_reuseaddr(sock->sk); +#endif +} + +static inline void ksmbd_tcp_rcv_timeout(struct socket *sock, s64 secs) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0) + struct __kernel_old_timeval tv = { .tv_sec = secs, .tv_usec = 0 }; + + kernel_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO_OLD, (char *)&tv, + sizeof(tv)); +#else + lock_sock(sock->sk); + if (secs && secs < MAX_SCHEDULE_TIMEOUT / HZ - 1) + sock->sk->sk_rcvtimeo = secs * HZ; + else + sock->sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT; + release_sock(sock->sk); +#endif +} + +static inline void ksmbd_tcp_snd_timeout(struct socket *sock, s64 secs) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0) + struct __kernel_old_timeval tv = { .tv_sec = secs, .tv_usec = 0 }; + + kernel_setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO_OLD, (char *)&tv, + sizeof(tv)); +#else + sock_set_sndtimeo(sock->sk, secs); +#endif +} + +static struct tcp_transport *alloc_transport(struct socket *client_sk) +{ + struct tcp_transport *t; + struct ksmbd_conn *conn; + + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (!t) + return NULL; + t->sock = client_sk; + + conn = ksmbd_conn_alloc(); + if (!conn) { + kfree(t); + return NULL; + } + + conn->transport = KSMBD_TRANS(t); + KSMBD_TRANS(t)->conn = conn; + KSMBD_TRANS(t)->ops = &ksmbd_tcp_transport_ops; + return t; +} + +static void free_transport(struct tcp_transport *t) +{ + kernel_sock_shutdown(t->sock, SHUT_RDWR); + sock_release(t->sock); + t->sock = NULL; + + ksmbd_conn_free(KSMBD_TRANS(t)->conn); + kfree(t->iov); + kfree(t); +} + +/** + * kvec_array_init() - initialize a IO vector segment + * @new: IO vector to be initialized + * @iov: base IO vector + * @nr_segs: number of segments in base iov + * @bytes: total iovec length so far for read + * + * Return: Number of IO segments + */ +static unsigned int kvec_array_init(struct kvec *new, struct kvec *iov, + unsigned int nr_segs, size_t bytes) +{ + size_t base = 0; + + while (bytes || !iov->iov_len) { + int copy = min(bytes, iov->iov_len); + + bytes -= copy; + base += copy; + if (iov->iov_len == base) { + iov++; + nr_segs--; + base = 0; + } + } + + memcpy(new, iov, sizeof(*iov) * nr_segs); + new->iov_base += base; + new->iov_len -= base; + return nr_segs; +} + +/** + * get_conn_iovec() - get connection iovec for reading from socket + * @t: TCP transport instance + * @nr_segs: number of segments in iov + * + * Return: return existing or newly allocate iovec + */ +static struct kvec *get_conn_iovec(struct tcp_transport *t, unsigned int nr_segs) +{ + struct kvec *new_iov; + + if (t->iov && nr_segs <= t->nr_iov) + return t->iov; + + /* not big enough -- allocate a new one and release the old */ + new_iov = kmalloc_array(nr_segs, sizeof(*new_iov), GFP_KERNEL); + if (new_iov) { + kfree(t->iov); + t->iov = new_iov; + t->nr_iov = nr_segs; + } + return new_iov; +} + +static unsigned short ksmbd_tcp_get_port(const struct sockaddr *sa) +{ + switch (sa->sa_family) { + case AF_INET: + return ntohs(((struct sockaddr_in *)sa)->sin_port); + case AF_INET6: + return ntohs(((struct sockaddr_in6 *)sa)->sin6_port); + } + return 0; +} + +/** + * ksmbd_tcp_new_connection() - create a new tcp session on mount + * @client_sk: socket associated with new connection + * + * whenever a new connection is requested, create a conn thread + * (session thread) to handle new incoming smb requests from the connection + * + * Return: 0 on success, otherwise error + */ +static int ksmbd_tcp_new_connection(struct socket *client_sk) +{ + struct sockaddr *csin; + int rc = 0; + struct tcp_transport *t; + + t = alloc_transport(client_sk); + if (!t) { + sock_release(client_sk); + return -ENOMEM; + } + + csin = KSMBD_TCP_PEER_SOCKADDR(KSMBD_TRANS(t)->conn); + + if (kernel_getpeername(client_sk, csin) < 0) { + pr_err("client ip resolution failed\n"); + rc = -EINVAL; + goto out_error; + } + KSMBD_TRANS(t)->handler = kthread_run(ksmbd_conn_handler_loop, + KSMBD_TRANS(t)->conn, + "ksmbd:%u", + ksmbd_tcp_get_port(csin)); + if (IS_ERR(KSMBD_TRANS(t)->handler)) { + pr_err("cannot start conn thread\n"); + rc = PTR_ERR(KSMBD_TRANS(t)->handler); + free_transport(t); + } + return rc; + +out_error: + free_transport(t); + return rc; +} + +/** + * ksmbd_kthread_fn() - listen to new SMB connections and callback server + * @p: arguments to forker thread + * + * Return: 0 on success, error number otherwise + */ +static int ksmbd_kthread_fn(void *p) +{ + struct socket *client_sk = NULL; + struct interface *iface = (struct interface *)p; + int ret; + + while (!kthread_should_stop()) { + mutex_lock(&iface->sock_release_lock); + if (!iface->ksmbd_socket) { + mutex_unlock(&iface->sock_release_lock); + break; + } + ret = kernel_accept(iface->ksmbd_socket, &client_sk, + SOCK_NONBLOCK); + mutex_unlock(&iface->sock_release_lock); + if (ret) { + if (ret == -EAGAIN) + /* check for new connections every 100 msecs */ + schedule_timeout_interruptible(HZ / 10); + continue; + } + + if (server_conf.max_connections && + atomic_inc_return(&active_num_conn) >= server_conf.max_connections) { + pr_info_ratelimited("Limit the maximum number of connections(%u)\n", + atomic_read(&active_num_conn)); + atomic_dec(&active_num_conn); + sock_release(client_sk); + continue; + } + + ksmbd_debug(CONN, "connect success: accepted new connection\n"); + client_sk->sk->sk_rcvtimeo = KSMBD_TCP_RECV_TIMEOUT; + client_sk->sk->sk_sndtimeo = KSMBD_TCP_SEND_TIMEOUT; + + ksmbd_tcp_new_connection(client_sk); + } + + ksmbd_debug(CONN, "releasing socket\n"); + return 0; +} + +/** + * ksmbd_tcp_run_kthread() - start forker thread + * @iface: pointer to struct interface + * + * start forker thread(ksmbd/0) at module init time to listen + * on port 445 for new SMB connection requests. It creates per connection + * server threads(ksmbd/x) + * + * Return: 0 on success or error number + */ +static int ksmbd_tcp_run_kthread(struct interface *iface) +{ + int rc; + struct task_struct *kthread; + + kthread = kthread_run(ksmbd_kthread_fn, (void *)iface, "ksmbd-%s", + iface->name); + if (IS_ERR(kthread)) { + rc = PTR_ERR(kthread); + return rc; + } + iface->ksmbd_kthread = kthread; + + return 0; +} + +/** + * ksmbd_tcp_readv() - read data from socket in given iovec + * @t: TCP transport instance + * @iov_orig: base IO vector + * @nr_segs: number of segments in base iov + * @to_read: number of bytes to read from socket + * + * Return: on success return number of bytes read from socket, + * otherwise return error number + */ +static int ksmbd_tcp_readv(struct tcp_transport *t, struct kvec *iov_orig, + unsigned int nr_segs, unsigned int to_read) +{ + int length = 0; + int total_read; + unsigned int segs; + struct msghdr ksmbd_msg; + struct kvec *iov; + struct ksmbd_conn *conn = KSMBD_TRANS(t)->conn; + int max_retry = 2; + + iov = get_conn_iovec(t, nr_segs); + if (!iov) + return -ENOMEM; + + ksmbd_msg.msg_control = NULL; + ksmbd_msg.msg_controllen = 0; + + for (total_read = 0; to_read; total_read += length, to_read -= length) { + try_to_freeze(); + + if (!ksmbd_conn_alive(conn)) { + total_read = -ESHUTDOWN; + break; + } + segs = kvec_array_init(iov, iov_orig, nr_segs, total_read); + + length = kernel_recvmsg(t->sock, &ksmbd_msg, + iov, segs, to_read, 0); + + if (length == -EINTR) { + total_read = -ESHUTDOWN; + break; + } else if (conn->status == KSMBD_SESS_NEED_RECONNECT) { + total_read = -EAGAIN; + break; + } else if ((length == -ERESTARTSYS || length == -EAGAIN) && + max_retry) { + usleep_range(1000, 2000); + length = 0; + max_retry--; + continue; + } else if (length <= 0) { + total_read = -EAGAIN; + break; + } + } + return total_read; +} + +/** + * ksmbd_tcp_read() - read data from socket in given buffer + * @t: TCP transport instance + * @buf: buffer to store read data from socket + * @to_read: number of bytes to read from socket + * + * Return: on success return number of bytes read from socket, + * otherwise return error number + */ +static int ksmbd_tcp_read(struct ksmbd_transport *t, char *buf, unsigned int to_read) +{ + struct kvec iov; + + iov.iov_base = buf; + iov.iov_len = to_read; + + return ksmbd_tcp_readv(TCP_TRANS(t), &iov, 1, to_read); +} + +static int ksmbd_tcp_writev(struct ksmbd_transport *t, struct kvec *iov, + int nvecs, int size, bool need_invalidate, + unsigned int remote_key) + +{ + struct msghdr smb_msg = {.msg_flags = MSG_NOSIGNAL}; + + return kernel_sendmsg(TCP_TRANS(t)->sock, &smb_msg, iov, nvecs, size); +} + +static void ksmbd_tcp_disconnect(struct ksmbd_transport *t) +{ + free_transport(TCP_TRANS(t)); + if (server_conf.max_connections) + atomic_dec(&active_num_conn); +} + +static void tcp_destroy_socket(struct socket *ksmbd_socket) +{ + int ret; + + if (!ksmbd_socket) + return; + + /* set zero to timeout */ + ksmbd_tcp_rcv_timeout(ksmbd_socket, 0); + ksmbd_tcp_snd_timeout(ksmbd_socket, 0); + + ret = kernel_sock_shutdown(ksmbd_socket, SHUT_RDWR); + if (ret) + pr_err("Failed to shutdown socket: %d\n", ret); + sock_release(ksmbd_socket); +} + +/** + * create_socket - create socket for ksmbd/0 + * + * Return: 0 on success, error number otherwise + */ +static int create_socket(struct interface *iface) +{ + int ret; + struct sockaddr_in6 sin6; + struct sockaddr_in sin; + struct socket *ksmbd_socket; + bool ipv4 = false; + + ret = sock_create(PF_INET6, SOCK_STREAM, IPPROTO_TCP, &ksmbd_socket); + if (ret) { + if (ret != -EAFNOSUPPORT) + pr_err("Can't create socket for ipv6, fallback to ipv4: %d\n", ret); + ret = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, + &ksmbd_socket); + if (ret) { + pr_err("Can't create socket for ipv4: %d\n", ret); + goto out_clear; + } + + sin.sin_family = PF_INET; + sin.sin_addr.s_addr = htonl(INADDR_ANY); + sin.sin_port = htons(server_conf.tcp_port); + ipv4 = true; + } else { + sin6.sin6_family = PF_INET6; + sin6.sin6_addr = in6addr_any; + sin6.sin6_port = htons(server_conf.tcp_port); + } + + ksmbd_tcp_nodelay(ksmbd_socket); + ksmbd_tcp_reuseaddr(ksmbd_socket); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0) + ret = kernel_setsockopt(ksmbd_socket, + SOL_SOCKET, + SO_BINDTODEVICE, + iface->name, + strlen(iface->name)); +#else + ret = sock_setsockopt(ksmbd_socket, + SOL_SOCKET, + SO_BINDTODEVICE, +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0) + (char __user *)iface->name, +#else + KERNEL_SOCKPTR(iface->name), +#endif + strlen(iface->name)); +#endif + if (ret != -ENODEV && ret < 0) { + pr_err("Failed to set SO_BINDTODEVICE: %d\n", ret); + goto out_error; + } + + if (ipv4) + ret = kernel_bind(ksmbd_socket, (struct sockaddr *)&sin, + sizeof(sin)); + else + ret = kernel_bind(ksmbd_socket, (struct sockaddr *)&sin6, + sizeof(sin6)); + if (ret) { + pr_err("Failed to bind socket: %d\n", ret); + goto out_error; + } + + ksmbd_socket->sk->sk_rcvtimeo = KSMBD_TCP_RECV_TIMEOUT; + ksmbd_socket->sk->sk_sndtimeo = KSMBD_TCP_SEND_TIMEOUT; + + ret = kernel_listen(ksmbd_socket, KSMBD_SOCKET_BACKLOG); + if (ret) { + pr_err("Port listen() error: %d\n", ret); + goto out_error; + } + + iface->ksmbd_socket = ksmbd_socket; + ret = ksmbd_tcp_run_kthread(iface); + if (ret) { + pr_err("Can't start ksmbd main kthread: %d\n", ret); + goto out_error; + } + iface->state = IFACE_STATE_CONFIGURED; + + return 0; + +out_error: + tcp_destroy_socket(ksmbd_socket); +out_clear: + iface->ksmbd_socket = NULL; + return ret; +} + +static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct net_device *netdev = netdev_notifier_info_to_dev(ptr); + struct interface *iface; + int ret, found = 0; + + switch (event) { + case NETDEV_UP: + if (netdev->priv_flags & IFF_BRIDGE_PORT) + return NOTIFY_OK; + + list_for_each_entry(iface, &iface_list, entry) { + if (!strcmp(iface->name, netdev->name)) { + found = 1; + if (iface->state != IFACE_STATE_DOWN) + break; + ret = create_socket(iface); + if (ret) + return NOTIFY_OK; + break; + } + } + if (!found && bind_additional_ifaces) { + iface = alloc_iface(kstrdup(netdev->name, GFP_KERNEL)); + if (!iface) + return NOTIFY_OK; + ret = create_socket(iface); + if (ret) + break; + } + break; + case NETDEV_DOWN: + list_for_each_entry(iface, &iface_list, entry) { + if (!strcmp(iface->name, netdev->name) && + iface->state == IFACE_STATE_CONFIGURED) { + tcp_stop_kthread(iface->ksmbd_kthread); + iface->ksmbd_kthread = NULL; + mutex_lock(&iface->sock_release_lock); + tcp_destroy_socket(iface->ksmbd_socket); + iface->ksmbd_socket = NULL; + mutex_unlock(&iface->sock_release_lock); + + iface->state = IFACE_STATE_DOWN; + break; + } + } + break; + } + + return NOTIFY_DONE; +} + +static struct notifier_block ksmbd_netdev_notifier = { + .notifier_call = ksmbd_netdev_event, +}; + +int ksmbd_tcp_init(void) +{ + register_netdevice_notifier(&ksmbd_netdev_notifier); + + return 0; +} + +static void tcp_stop_kthread(struct task_struct *kthread) +{ + int ret; + + if (!kthread) + return; + + ret = kthread_stop(kthread); + if (ret) + pr_err("failed to stop forker thread\n"); +} + +void ksmbd_tcp_destroy(void) +{ + struct interface *iface, *tmp; + + unregister_netdevice_notifier(&ksmbd_netdev_notifier); + + list_for_each_entry_safe(iface, tmp, &iface_list, entry) { + list_del(&iface->entry); + kfree(iface->name); + kfree(iface); + } +} + +static struct interface *alloc_iface(char *ifname) +{ + struct interface *iface; + + if (!ifname) + return NULL; + + iface = kzalloc(sizeof(struct interface), GFP_KERNEL); + if (!iface) { + kfree(ifname); + return NULL; + } + + iface->name = ifname; + iface->state = IFACE_STATE_DOWN; + list_add(&iface->entry, &iface_list); + mutex_init(&iface->sock_release_lock); + return iface; +} + +int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz) +{ + int sz = 0; + + if (!ifc_list_sz) { + struct net_device *netdev; + + rtnl_lock(); + for_each_netdev(&init_net, netdev) { + if (netdev->priv_flags & IFF_BRIDGE_PORT) + continue; + if (!alloc_iface(kstrdup(netdev->name, GFP_KERNEL))) + return -ENOMEM; + } + rtnl_unlock(); + bind_additional_ifaces = 1; + return 0; + } + + while (ifc_list_sz > 0) { + if (!alloc_iface(kstrdup(ifc_list, GFP_KERNEL))) + return -ENOMEM; + + sz = strlen(ifc_list); + if (!sz) + break; + + ifc_list += sz + 1; + ifc_list_sz -= (sz + 1); + } + + bind_additional_ifaces = 0; + + return 0; +} + +static struct ksmbd_transport_ops ksmbd_tcp_transport_ops = { + .read = ksmbd_tcp_read, + .writev = ksmbd_tcp_writev, + .disconnect = ksmbd_tcp_disconnect, +}; diff --git a/fs/ksmbd/transport_tcp.h b/fs/ksmbd/transport_tcp.h new file mode 100644 index 0000000000000..e338bebe322f1 --- /dev/null +++ b/fs/ksmbd/transport_tcp.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_TRANSPORT_TCP_H__ +#define __KSMBD_TRANSPORT_TCP_H__ + +int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz); +int ksmbd_tcp_init(void); +void ksmbd_tcp_destroy(void); + +#endif /* __KSMBD_TRANSPORT_TCP_H__ */ diff --git a/fs/ksmbd/unicode.c b/fs/ksmbd/unicode.c new file mode 100644 index 0000000000000..b21b5c1b239f4 --- /dev/null +++ b/fs/ksmbd/unicode.c @@ -0,0 +1,402 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Some of the source code in this file came from fs/cifs/cifs_unicode.c + * + * Copyright (c) International Business Machines Corp., 2000,2009 + * Modified by Steve French (sfrench@us.ibm.com) + * Modified by Namjae Jeon (linkinjeon@kernel.org) + */ +#include +#include +#include +#include "glob.h" +#include "unicode.h" +#include "uniupr.h" +#include "smb_common.h" + +#ifdef CONFIG_SMB_INSECURE_SERVER +int smb1_utf16_name_length(const __le16 *from, int maxbytes) +{ + int i, len = 0; + int maxwords = maxbytes / 2; + __u16 ftmp; + + for (i = 0; i < maxwords; i++) { + ftmp = get_unaligned_le16(&from[i]); + len += 2; + if (ftmp == 0) + break; + } + + return len; +} +#endif + +/* + * smb_utf16_bytes() - how long will a string be after conversion? + * @from: pointer to input string + * @maxbytes: don't go past this many bytes of input string + * @codepage: destination codepage + * + * Walk a utf16le string and return the number of bytes that the string will + * be after being converted to the given charset, not including any null + * termination required. Don't walk past maxbytes in the source buffer. + * + * Return: string length after conversion + */ +static int smb_utf16_bytes(const __le16 *from, int maxbytes, + const struct nls_table *codepage) +{ + int i; + int charlen, outlen = 0; + int maxwords = maxbytes / 2; + char tmp[NLS_MAX_CHARSET_SIZE]; + __u16 ftmp; + + for (i = 0; i < maxwords; i++) { + ftmp = get_unaligned_le16(&from[i]); + if (ftmp == 0) + break; + + charlen = codepage->uni2char(ftmp, tmp, NLS_MAX_CHARSET_SIZE); + if (charlen > 0) + outlen += charlen; + else + outlen++; + } + + return outlen; +} + +/* + * cifs_mapchar() - convert a host-endian char to proper char in codepage + * @target: where converted character should be copied + * @src_char: 2 byte host-endian source character + * @cp: codepage to which character should be converted + * @mapchar: should character be mapped according to mapchars mount option? + * + * This function handles the conversion of a single character. It is the + * responsibility of the caller to ensure that the target buffer is large + * enough to hold the result of the conversion (at least NLS_MAX_CHARSET_SIZE). + * + * Return: string length after conversion + */ +static int +cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp, + bool mapchar) +{ + int len = 1; + + if (!mapchar) + goto cp_convert; + + /* + * BB: Cannot handle remapping UNI_SLASH until all the calls to + * build_path_from_dentry are modified, as they use slash as + * separator. + */ + switch (src_char) { + case UNI_COLON: + *target = ':'; + break; + case UNI_ASTERISK: + *target = '*'; + break; + case UNI_QUESTION: + *target = '?'; + break; + case UNI_PIPE: + *target = '|'; + break; + case UNI_GRTRTHAN: + *target = '>'; + break; + case UNI_LESSTHAN: + *target = '<'; + break; + default: + goto cp_convert; + } + +out: + return len; + +cp_convert: + len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE); + if (len <= 0) { + *target = '?'; + len = 1; + } + + goto out; +} + +/* + * is_char_allowed() - check for valid character + * @ch: input character to be checked + * + * Return: 1 if char is allowed, otherwise 0 + */ +static inline int is_char_allowed(char *ch) +{ + /* check for control chars, wildcards etc. */ + if (!(*ch & 0x80) && + (*ch <= 0x1f || + *ch == '?' || *ch == '"' || *ch == '<' || + *ch == '>' || *ch == '|')) + return 0; + + return 1; +} + +/* + * smb_from_utf16() - convert utf16le string to local charset + * @to: destination buffer + * @from: source buffer + * @tolen: destination buffer size (in bytes) + * @fromlen: source buffer size (in bytes) + * @codepage: codepage to which characters should be converted + * @mapchar: should characters be remapped according to the mapchars option? + * + * Convert a little-endian utf16le string (as sent by the server) to a string + * in the provided codepage. The tolen and fromlen parameters are to ensure + * that the code doesn't walk off of the end of the buffer (which is always + * a danger if the alignment of the source buffer is off). The destination + * string is always properly null terminated and fits in the destination + * buffer. Returns the length of the destination string in bytes (including + * null terminator). + * + * Note that some windows versions actually send multiword UTF-16 characters + * instead of straight UTF16-2. The linux nls routines however aren't able to + * deal with those characters properly. In the event that we get some of + * those characters, they won't be translated properly. + * + * Return: string length after conversion + */ +static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen, + const struct nls_table *codepage, bool mapchar) +{ + int i, charlen, safelen; + int outlen = 0; + int nullsize = nls_nullsize(codepage); + int fromwords = fromlen / 2; + char tmp[NLS_MAX_CHARSET_SIZE]; + __u16 ftmp; + + /* + * because the chars can be of varying widths, we need to take care + * not to overflow the destination buffer when we get close to the + * end of it. Until we get to this offset, we don't need to check + * for overflow however. + */ + safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize); + + for (i = 0; i < fromwords; i++) { + ftmp = get_unaligned_le16(&from[i]); + if (ftmp == 0) + break; + + /* + * check to see if converting this character might make the + * conversion bleed into the null terminator + */ + if (outlen >= safelen) { + charlen = cifs_mapchar(tmp, ftmp, codepage, mapchar); + if ((outlen + charlen) > (tolen - nullsize)) + break; + } + + /* put converted char into 'to' buffer */ + charlen = cifs_mapchar(&to[outlen], ftmp, codepage, mapchar); + outlen += charlen; + } + + /* properly null-terminate string */ + for (i = 0; i < nullsize; i++) + to[outlen++] = 0; + + return outlen; +} + +/* + * smb_strtoUTF16() - Convert character string to unicode string + * @to: destination buffer + * @from: source buffer + * @len: destination buffer size (in bytes) + * @codepage: codepage to which characters should be converted + * + * Return: string length after conversion + */ +int smb_strtoUTF16(__le16 *to, const char *from, int len, + const struct nls_table *codepage) +{ + int charlen; + int i; + wchar_t wchar_to; /* needed to quiet sparse */ + + /* special case for utf8 to handle no plane0 chars */ + if (!strcmp(codepage->charset, "utf8")) { + /* + * convert utf8 -> utf16, we assume we have enough space + * as caller should have assumed conversion does not overflow + * in destination len is length in wchar_t units (16bits) + */ + i = utf8s_to_utf16s(from, len, UTF16_LITTLE_ENDIAN, + (wchar_t *)to, len); + + /* if success terminate and exit */ + if (i >= 0) + goto success; + /* + * if fails fall back to UCS encoding as this + * function should not return negative values + * currently can fail only if source contains + * invalid encoded characters + */ + } + + for (i = 0; len > 0 && *from; i++, from += charlen, len -= charlen) { + charlen = codepage->char2uni(from, len, &wchar_to); + if (charlen < 1) { + /* A question mark */ + wchar_to = 0x003f; + charlen = 1; + } + put_unaligned_le16(wchar_to, &to[i]); + } + +success: + put_unaligned_le16(0, &to[i]); + return i; +} + +/* + * smb_strndup_from_utf16() - copy a string from wire format to the local + * codepage + * @src: source string + * @maxlen: don't walk past this many bytes in the source string + * @is_unicode: is this a unicode string? + * @codepage: destination codepage + * + * Take a string given by the server, convert it to the local codepage and + * put it in a new buffer. Returns a pointer to the new string or NULL on + * error. + * + * Return: destination string buffer or error ptr + */ +char *smb_strndup_from_utf16(const char *src, const int maxlen, + const bool is_unicode, + const struct nls_table *codepage) +{ + int len, ret; + char *dst; + + if (is_unicode) { + len = smb_utf16_bytes((__le16 *)src, maxlen, codepage); + len += nls_nullsize(codepage); + dst = kmalloc(len, GFP_KERNEL); + if (!dst) + return ERR_PTR(-ENOMEM); + ret = smb_from_utf16(dst, (__le16 *)src, len, maxlen, codepage, + false); + if (ret < 0) { + kfree(dst); + return ERR_PTR(-EINVAL); + } + } else { + len = strnlen(src, maxlen); + len++; + dst = kmalloc(len, GFP_KERNEL); + if (!dst) + return ERR_PTR(-ENOMEM); + strscpy(dst, src, len); + } + + return dst; +} + +/* + * Convert 16 bit Unicode pathname to wire format from string in current code + * page. Conversion may involve remapping up the six characters that are + * only legal in POSIX-like OS (if they are present in the string). Path + * names are little endian 16 bit Unicode on the wire + */ +/* + * smbConvertToUTF16() - convert string from local charset to utf16 + * @target: destination buffer + * @source: source buffer + * @srclen: source buffer size (in bytes) + * @cp: codepage to which characters should be converted + * @mapchar: should characters be remapped according to the mapchars option? + * + * Convert 16 bit Unicode pathname to wire format from string in current code + * page. Conversion may involve remapping up the six characters that are + * only legal in POSIX-like OS (if they are present in the string). Path + * names are little endian 16 bit Unicode on the wire + * + * Return: char length after conversion + */ +int smbConvertToUTF16(__le16 *target, const char *source, int srclen, + const struct nls_table *cp, int mapchars) +{ + int i, j, charlen; + char src_char; + __le16 dst_char; + wchar_t tmp; + + if (!mapchars) + return smb_strtoUTF16(target, source, srclen, cp); + + for (i = 0, j = 0; i < srclen; j++) { + src_char = source[i]; + charlen = 1; + switch (src_char) { + case 0: + put_unaligned(0, &target[j]); + return j; + case ':': + dst_char = cpu_to_le16(UNI_COLON); + break; + case '*': + dst_char = cpu_to_le16(UNI_ASTERISK); + break; + case '?': + dst_char = cpu_to_le16(UNI_QUESTION); + break; + case '<': + dst_char = cpu_to_le16(UNI_LESSTHAN); + break; + case '>': + dst_char = cpu_to_le16(UNI_GRTRTHAN); + break; + case '|': + dst_char = cpu_to_le16(UNI_PIPE); + break; + /* + * FIXME: We can not handle remapping backslash (UNI_SLASH) + * until all the calls to build_path_from_dentry are modified, + * as they use backslash as separator. + */ + default: + charlen = cp->char2uni(source + i, srclen - i, &tmp); + dst_char = cpu_to_le16(tmp); + + /* + * if no match, use question mark, which at least in + * some cases serves as wild card + */ + if (charlen < 1) { + dst_char = cpu_to_le16(0x003f); + charlen = 1; + } + } + /* + * character may take more than one byte in the source string, + * but will take exactly two bytes in the target string + */ + i += charlen; + put_unaligned(dst_char, &target[j]); + } + + return j; +} diff --git a/fs/ksmbd/unicode.h b/fs/ksmbd/unicode.h new file mode 100644 index 0000000000000..9252b68f35bb8 --- /dev/null +++ b/fs/ksmbd/unicode.h @@ -0,0 +1,361 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Some of the source code in this file came from fs/cifs/cifs_unicode.c + * cifs_unicode: Unicode kernel case support + * + * Function: + * Convert a unicode character to upper or lower case using + * compressed tables. + * + * Copyright (c) International Business Machines Corp., 2000,2009 + * + * + * Notes: + * These APIs are based on the C library functions. The semantics + * should match the C functions but with expanded size operands. + * + * The upper/lower functions are based on a table created by mkupr. + * This is a compressed table of upper and lower case conversion. + * + */ +#ifndef _CIFS_UNICODE_H +#define _CIFS_UNICODE_H + +#include +#include +#include +#include + +#define UNIUPR_NOLOWER /* Example to not expand lower case tables */ + +/* + * Windows maps these to the user defined 16 bit Unicode range since they are + * reserved symbols (along with \ and /), otherwise illegal to store + * in filenames in NTFS + */ +#define UNI_ASTERISK ((__u16)('*' + 0xF000)) +#define UNI_QUESTION ((__u16)('?' + 0xF000)) +#define UNI_COLON ((__u16)(':' + 0xF000)) +#define UNI_GRTRTHAN ((__u16)('>' + 0xF000)) +#define UNI_LESSTHAN ((__u16)('<' + 0xF000)) +#define UNI_PIPE ((__u16)('|' + 0xF000)) +#define UNI_SLASH ((__u16)('\\' + 0xF000)) + +/* Just define what we want from uniupr.h. We don't want to define the tables + * in each source file. + */ +#ifndef UNICASERANGE_DEFINED +struct UniCaseRange { + wchar_t start; + wchar_t end; + signed char *table; +}; +#endif /* UNICASERANGE_DEFINED */ + +#ifndef UNIUPR_NOUPPER +extern signed char SmbUniUpperTable[512]; +extern const struct UniCaseRange SmbUniUpperRange[]; +#endif /* UNIUPR_NOUPPER */ + +#ifndef UNIUPR_NOLOWER +extern signed char CifsUniLowerTable[512]; +extern const struct UniCaseRange CifsUniLowerRange[]; +#endif /* UNIUPR_NOLOWER */ + +#ifdef __KERNEL__ +#ifdef CONFIG_SMB_INSECURE_SERVER +int smb1_utf16_name_length(const __le16 *from, int maxbytes); +#endif +int smb_strtoUTF16(__le16 *to, const char *from, int len, + const struct nls_table *codepage); +char *smb_strndup_from_utf16(const char *src, const int maxlen, + const bool is_unicode, + const struct nls_table *codepage); +int smbConvertToUTF16(__le16 *target, const char *source, int srclen, + const struct nls_table *cp, int mapchars); +char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename); +#endif + +/* + * UniStrcat: Concatenate the second string to the first + * + * Returns: + * Address of the first string + */ +static inline wchar_t *UniStrcat(wchar_t *ucs1, const wchar_t *ucs2) +{ + wchar_t *anchor = ucs1; /* save a pointer to start of ucs1 */ + + while (*ucs1++) + /*NULL*/; /* To end of first string */ + ucs1--; /* Return to the null */ + while ((*ucs1++ = *ucs2++)) + /*NULL*/; /* copy string 2 over */ + return anchor; +} + +/* + * UniStrchr: Find a character in a string + * + * Returns: + * Address of first occurrence of character in string + * or NULL if the character is not in the string + */ +static inline wchar_t *UniStrchr(const wchar_t *ucs, wchar_t uc) +{ + while ((*ucs != uc) && *ucs) + ucs++; + + if (*ucs == uc) + return (wchar_t *)ucs; + return NULL; +} + +/* + * UniStrcmp: Compare two strings + * + * Returns: + * < 0: First string is less than second + * = 0: Strings are equal + * > 0: First string is greater than second + */ +static inline int UniStrcmp(const wchar_t *ucs1, const wchar_t *ucs2) +{ + while ((*ucs1 == *ucs2) && *ucs1) { + ucs1++; + ucs2++; + } + return (int)*ucs1 - (int)*ucs2; +} + +/* + * UniStrcpy: Copy a string + */ +static inline wchar_t *UniStrcpy(wchar_t *ucs1, const wchar_t *ucs2) +{ + wchar_t *anchor = ucs1; /* save the start of result string */ + + while ((*ucs1++ = *ucs2++)) + /*NULL*/; + return anchor; +} + +/* + * UniStrlen: Return the length of a string (in 16 bit Unicode chars not bytes) + */ +static inline size_t UniStrlen(const wchar_t *ucs1) +{ + int i = 0; + + while (*ucs1++) + i++; + return i; +} + +/* + * UniStrnlen: Return the length (in 16 bit Unicode chars not bytes) of a + * string (length limited) + */ +static inline size_t UniStrnlen(const wchar_t *ucs1, int maxlen) +{ + int i = 0; + + while (*ucs1++) { + i++; + if (i >= maxlen) + break; + } + return i; +} + +/* + * UniStrncat: Concatenate length limited string + */ +static inline wchar_t *UniStrncat(wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + wchar_t *anchor = ucs1; /* save pointer to string 1 */ + + while (*ucs1++) + /*NULL*/; + ucs1--; /* point to null terminator of s1 */ + while (n-- && (*ucs1 = *ucs2)) { /* copy s2 after s1 */ + ucs1++; + ucs2++; + } + *ucs1 = 0; /* Null terminate the result */ + return anchor; +} + +/* + * UniStrncmp: Compare length limited string + */ +static inline int UniStrncmp(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + if (!n) + return 0; /* Null strings are equal */ + while ((*ucs1 == *ucs2) && *ucs1 && --n) { + ucs1++; + ucs2++; + } + return (int)*ucs1 - (int)*ucs2; +} + +/* + * UniStrncmp_le: Compare length limited string - native to little-endian + */ +static inline int +UniStrncmp_le(const wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + if (!n) + return 0; /* Null strings are equal */ + while ((*ucs1 == __le16_to_cpu(*ucs2)) && *ucs1 && --n) { + ucs1++; + ucs2++; + } + return (int)*ucs1 - (int)__le16_to_cpu(*ucs2); +} + +/* + * UniStrncpy: Copy length limited string with pad + */ +static inline wchar_t *UniStrncpy(wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + wchar_t *anchor = ucs1; + + while (n-- && *ucs2) /* Copy the strings */ + *ucs1++ = *ucs2++; + + n++; + while (n--) /* Pad with nulls */ + *ucs1++ = 0; + return anchor; +} + +/* + * UniStrncpy_le: Copy length limited string with pad to little-endian + */ +static inline wchar_t *UniStrncpy_le(wchar_t *ucs1, const wchar_t *ucs2, size_t n) +{ + wchar_t *anchor = ucs1; + + while (n-- && *ucs2) /* Copy the strings */ + *ucs1++ = __le16_to_cpu(*ucs2++); + + n++; + while (n--) /* Pad with nulls */ + *ucs1++ = 0; + return anchor; +} + +/* + * UniStrstr: Find a string in a string + * + * Returns: + * Address of first match found + * NULL if no matching string is found + */ +static inline wchar_t *UniStrstr(const wchar_t *ucs1, const wchar_t *ucs2) +{ + const wchar_t *anchor1 = ucs1; + const wchar_t *anchor2 = ucs2; + + while (*ucs1) { + if (*ucs1 == *ucs2) { + /* Partial match found */ + ucs1++; + ucs2++; + } else { + if (!*ucs2) /* Match found */ + return (wchar_t *)anchor1; + ucs1 = ++anchor1; /* No match */ + ucs2 = anchor2; + } + } + + if (!*ucs2) /* Both end together */ + return (wchar_t *)anchor1; /* Match found */ + return NULL; /* No match */ +} + +#ifndef UNIUPR_NOUPPER +/* + * UniToupper: Convert a unicode character to upper case + */ +static inline wchar_t UniToupper(register wchar_t uc) +{ + register const struct UniCaseRange *rp; + + if (uc < sizeof(SmbUniUpperTable)) { + /* Latin characters */ + return uc + SmbUniUpperTable[uc]; /* Use base tables */ + } + + rp = SmbUniUpperRange; /* Use range tables */ + while (rp->start) { + if (uc < rp->start) /* Before start of range */ + return uc; /* Uppercase = input */ + if (uc <= rp->end) /* In range */ + return uc + rp->table[uc - rp->start]; + rp++; /* Try next range */ + } + return uc; /* Past last range */ +} + +/* + * UniStrupr: Upper case a unicode string + */ +static inline __le16 *UniStrupr(register __le16 *upin) +{ + register __le16 *up; + + up = upin; + while (*up) { /* For all characters */ + *up = cpu_to_le16(UniToupper(le16_to_cpu(*up))); + up++; + } + return upin; /* Return input pointer */ +} +#endif /* UNIUPR_NOUPPER */ + +#ifndef UNIUPR_NOLOWER +/* + * UniTolower: Convert a unicode character to lower case + */ +static inline wchar_t UniTolower(register wchar_t uc) +{ + register const struct UniCaseRange *rp; + + if (uc < sizeof(CifsUniLowerTable)) { + /* Latin characters */ + return uc + CifsUniLowerTable[uc]; /* Use base tables */ + } + + rp = CifsUniLowerRange; /* Use range tables */ + while (rp->start) { + if (uc < rp->start) /* Before start of range */ + return uc; /* Uppercase = input */ + if (uc <= rp->end) /* In range */ + return uc + rp->table[uc - rp->start]; + rp++; /* Try next range */ + } + return uc; /* Past last range */ +} + +/* + * UniStrlwr: Lower case a unicode string + */ +static inline wchar_t *UniStrlwr(register wchar_t *upin) +{ + register wchar_t *up; + + up = upin; + while (*up) { /* For all characters */ + *up = UniTolower(*up); + up++; + } + return upin; /* Return input pointer */ +} + +#endif + +#endif /* _CIFS_UNICODE_H */ diff --git a/fs/ksmbd/uniupr.h b/fs/ksmbd/uniupr.h new file mode 100644 index 0000000000000..26583b776897b --- /dev/null +++ b/fs/ksmbd/uniupr.h @@ -0,0 +1,268 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Some of the source code in this file came from fs/cifs/uniupr.h + * Copyright (c) International Business Machines Corp., 2000,2002 + * + * uniupr.h - Unicode compressed case ranges + * + */ +#ifndef __KSMBD_UNIUPR_H +#define __KSMBD_UNIUPR_H + +#ifndef UNIUPR_NOUPPER +/* + * Latin upper case + */ +signed char SmbUniUpperTable[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 040-04f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 050-05f */ + 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, /* 060-06f */ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, 0, 0, 0, 0, 0, /* 070-07f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0c0-0cf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0d0-0df */ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, -32, /* 0e0-0ef */ + -32, -32, -32, -32, -32, -32, -32, 0, -32, -32, + -32, -32, -32, -32, -32, 121, /* 0f0-0ff */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 100-10f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 110-11f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 120-12f */ + 0, 0, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 130-13f */ + -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, /* 140-14f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 150-15f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 160-16f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 170-17f */ + 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, /* 180-18f */ + 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, /* 190-19f */ + 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, /* 1a0-1af */ + -1, 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, /* 1b0-1bf */ + 0, 0, 0, 0, 0, -1, -2, 0, -1, -2, 0, -1, -2, 0, -1, 0, /* 1c0-1cf */ + -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, -79, 0, -1, /* 1d0-1df */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e0-1ef */ + 0, 0, -1, -2, 0, -1, 0, 0, 0, -1, 0, -1, 0, -1, 0, -1, /* 1f0-1ff */ +}; + +/* Upper case range - Greek */ +static signed char UniCaseRangeU03a0[47] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -38, -37, -37, -37, /* 3a0-3af */ + 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, /* 3b0-3bf */ + -32, -32, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -64, + -63, -63, +}; + +/* Upper case range - Cyrillic */ +static signed char UniCaseRangeU0430[48] = { + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, /* 430-43f */ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, /* 440-44f */ + 0, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, + -80, -80, 0, -80, -80, /* 450-45f */ +}; + +/* Upper case range - Extended cyrillic */ +static signed char UniCaseRangeU0490[61] = { + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 490-49f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4a0-4af */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4b0-4bf */ + 0, 0, -1, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, +}; + +/* Upper case range - Extended latin and greek */ +static signed char UniCaseRangeU1e00[509] = { + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e00-1e0f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e10-1e1f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e20-1e2f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e30-1e3f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e40-1e4f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e50-1e5f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e60-1e6f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e70-1e7f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e80-1e8f */ + 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, -59, 0, -1, 0, -1, /* 1e90-1e9f */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ea0-1eaf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1eb0-1ebf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ec0-1ecf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ed0-1edf */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ee0-1eef */ + 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f00-1f0f */ + 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f10-1f1f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f20-1f2f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f30-1f3f */ + 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f40-1f4f */ + 0, 8, 0, 8, 0, 8, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f50-1f5f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f60-1f6f */ + 74, 74, 86, 86, 86, 86, 100, 100, 0, 0, 112, 112, + 126, 126, 0, 0, /* 1f70-1f7f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f80-1f8f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f90-1f9f */ + 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fa0-1faf */ + 8, 8, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fb0-1fbf */ + 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fc0-1fcf */ + 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fd0-1fdf */ + 8, 8, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fe0-1fef */ + 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/* Upper case range - Wide latin */ +static signed char UniCaseRangeUff40[27] = { + 0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, /* ff40-ff4f */ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, +}; + +/* + * Upper Case Range + */ +const struct UniCaseRange SmbUniUpperRange[] = { + {0x03a0, 0x03ce, UniCaseRangeU03a0}, + {0x0430, 0x045f, UniCaseRangeU0430}, + {0x0490, 0x04cc, UniCaseRangeU0490}, + {0x1e00, 0x1ffc, UniCaseRangeU1e00}, + {0xff40, 0xff5a, UniCaseRangeUff40}, + {0} +}; +#endif + +#ifndef UNIUPR_NOLOWER +/* + * Latin lower case + */ +signed char CifsUniLowerTable[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */ + 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, /* 040-04f */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0, 0, + 0, 0, 0, /* 050-05f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 060-06f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 070-07f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, /* 0c0-0cf */ + 32, 32, 32, 32, 32, 32, 32, 0, 32, 32, 32, 32, + 32, 32, 32, 0, /* 0d0-0df */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0e0-0ef */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0f0-0ff */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 100-10f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 110-11f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 120-12f */ + 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, /* 130-13f */ + 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, /* 140-14f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 150-15f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 160-16f */ + 1, 0, 1, 0, 1, 0, 1, 0, -121, 1, 0, 1, 0, 1, 0, + 0, /* 170-17f */ + 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 79, + 0, /* 180-18f */ + 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 190-19f */ + 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, /* 1a0-1af */ + 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, /* 1b0-1bf */ + 0, 0, 0, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 1, 0, 1, /* 1c0-1cf */ + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, /* 1d0-1df */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e0-1ef */ + 0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1f0-1ff */ +}; + +/* Lower case range - Greek */ +static signed char UniCaseRangeL0380[44] = { + 0, 0, 0, 0, 0, 0, 38, 0, 37, 37, 37, 0, 64, 0, 63, 63, /* 380-38f */ + 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, /* 390-39f */ + 32, 32, 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, +}; + +/* Lower case range - Cyrillic */ +static signed char UniCaseRangeL0400[48] = { + 0, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 0, 80, 80, /* 400-40f */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, /* 410-41f */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, /* 420-42f */ +}; + +/* Lower case range - Extended cyrillic */ +static signed char UniCaseRangeL0490[60] = { + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 490-49f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4a0-4af */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4b0-4bf */ + 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, +}; + +/* Lower case range - Extended latin and greek */ +static signed char UniCaseRangeL1e00[504] = { + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e00-1e0f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e10-1e1f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e20-1e2f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e30-1e3f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e40-1e4f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e50-1e5f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e60-1e6f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e70-1e7f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e80-1e8f */ + 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 1e90-1e9f */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ea0-1eaf */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1eb0-1ebf */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ec0-1ecf */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ed0-1edf */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ee0-1eef */ + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f00-1f0f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f10-1f1f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f20-1f2f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f30-1f3f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f40-1f4f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, -8, 0, -8, 0, -8, 0, -8, /* 1f50-1f5f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f60-1f6f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f70-1f7f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f80-1f8f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f90-1f9f */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1fa0-1faf */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -74, -74, -9, 0, 0, 0, /* 1fb0-1fbf */ + 0, 0, 0, 0, 0, 0, 0, 0, -86, -86, -86, -86, -9, 0, + 0, 0, /* 1fc0-1fcf */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -100, -100, 0, 0, 0, 0, /* 1fd0-1fdf */ + 0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -112, -112, -7, 0, + 0, 0, /* 1fe0-1fef */ + 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/* Lower case range - Wide latin */ +static signed char UniCaseRangeLff20[27] = { + 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, /* ff20-ff2f */ + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, +}; + +/* + * Lower Case Range + */ +const struct UniCaseRange CifsUniLowerRange[] = { + {0x0380, 0x03ab, UniCaseRangeL0380}, + {0x0400, 0x042f, UniCaseRangeL0400}, + {0x0490, 0x04cb, UniCaseRangeL0490}, + {0x1e00, 0x1ff7, UniCaseRangeL1e00}, + {0xff20, 0xff3a, UniCaseRangeLff20}, + {0} +}; +#endif + +#endif /* __KSMBD_UNIUPR_H */ diff --git a/fs/ksmbd/vfs.c b/fs/ksmbd/vfs.c new file mode 100644 index 0000000000000..17f3806d43c8f --- /dev/null +++ b/fs/ksmbd/vfs.c @@ -0,0 +1,2531 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 18, 0) +#include +#endif +#include +#include +#include +#include +#include +#include + +#include "glob.h" +#include "oplock.h" +#include "connection.h" +#include "vfs.h" +#include "vfs_cache.h" +#include "smbacl.h" +#include "ndr.h" +#include "auth.h" +#include "misc.h" + +#include "smb_common.h" +#include "mgmt/share_config.h" +#include "mgmt/tree_connect.h" +#include "mgmt/user_session.h" +#include "mgmt/user_config.h" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +extern int vfs_path_lookup(struct dentry *, struct vfsmount *, + const char *, unsigned int, struct path *); +#endif + +static char *extract_last_component(char *path) +{ + char *p = strrchr(path, '/'); + + if (p && p[1] != '\0') { + *p = '\0'; + p++; + } else { + p = NULL; + } + return p; +} + +static void ksmbd_vfs_inherit_owner(struct ksmbd_work *work, + struct inode *parent_inode, + struct inode *inode) +{ + if (!test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_INHERIT_OWNER)) + return; + + i_uid_write(inode, i_uid_read(parent_inode)); +} + +/** + * ksmbd_vfs_lock_parent() - lock parent dentry if it is stable + * + * the parent dentry got by dget_parent or @parent could be + * unstable, we try to lock a parent inode and lookup the + * child dentry again. + * + * the reference count of @parent isn't incremented. + */ +int ksmbd_vfs_lock_parent(struct user_namespace *user_ns, struct dentry *parent, + struct dentry *child) +{ + struct dentry *dentry; + int ret = 0; + + inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) + dentry = lookup_one(user_ns, child->d_name.name, parent, + child->d_name.len); +#else + dentry = lookup_one_len(child->d_name.name, parent, + child->d_name.len); +#endif + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); + goto out_err; + } + + if (dentry != child) { + ret = -ESTALE; + dput(dentry); + goto out_err; + } + + dput(dentry); + return 0; +out_err: + inode_unlock(d_inode(parent)); + return ret; +} + +int ksmbd_vfs_may_delete(struct user_namespace *user_ns, + struct dentry *dentry) +{ + struct dentry *parent; + int ret; + + parent = dget_parent(dentry); + ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry); + if (ret) { + dput(parent); + return ret; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + ret = inode_permission(user_ns, d_inode(parent), + MAY_EXEC | MAY_WRITE); +#else + ret = inode_permission(d_inode(parent), MAY_EXEC | MAY_WRITE); +#endif + + inode_unlock(d_inode(parent)); + dput(parent); + return ret; +} + +int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, + struct dentry *dentry, __le32 *daccess) +{ + struct dentry *parent; + int ret = 0; + + *daccess = cpu_to_le32(FILE_READ_ATTRIBUTES | READ_CONTROL); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_WRITE)) +#else + if (!inode_permission(d_inode(dentry), MAY_OPEN | MAY_WRITE)) +#endif + *daccess |= cpu_to_le32(WRITE_DAC | WRITE_OWNER | SYNCHRONIZE | + FILE_WRITE_DATA | FILE_APPEND_DATA | + FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | + FILE_DELETE_CHILD); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_READ)) +#else + if (!inode_permission(d_inode(dentry), MAY_OPEN | MAY_READ)) +#endif + *daccess |= FILE_READ_DATA_LE | FILE_READ_EA_LE; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + if (!inode_permission(user_ns, d_inode(dentry), MAY_OPEN | MAY_EXEC)) +#else + if (!inode_permission(d_inode(dentry), MAY_OPEN | MAY_EXEC)) +#endif + *daccess |= FILE_EXECUTE_LE; + + parent = dget_parent(dentry); + ret = ksmbd_vfs_lock_parent(user_ns, parent, dentry); + if (ret) { + dput(parent); + return ret; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + if (!inode_permission(user_ns, d_inode(parent), MAY_EXEC | MAY_WRITE)) +#else + if (!inode_permission(d_inode(parent), MAY_EXEC | MAY_WRITE)) +#endif + *daccess |= FILE_DELETE_LE; + + inode_unlock(d_inode(parent)); + dput(parent); + return ret; +} + +/** + * ksmbd_vfs_create() - vfs helper for smb create file + * @work: work + * @name: file name that is relative to share + * @mode: file create mode + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode) +{ + struct path path; + struct dentry *dentry; + int err; + + dentry = ksmbd_vfs_kern_path_create(work, name, + LOOKUP_NO_SYMLINKS, &path); + if (IS_ERR(dentry)) { + err = PTR_ERR(dentry); + if (err != -ENOENT) + pr_err("path create failed for %s, err %d\n", + name, err); + return err; + } + + mode |= S_IFREG; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + err = vfs_create(mnt_user_ns(path.mnt), d_inode(path.dentry), dentry, mode, true); +#else + err = vfs_create(d_inode(path.dentry), dentry, mode, true); +#endif + if (!err) { + ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), + d_inode(dentry)); + } else { + pr_err("File(%s): creation failed (err:%d)\n", name, err); + } + done_path_create(&path, dentry); + return err; +} + +/** + * ksmbd_vfs_mkdir() - vfs helper for smb create directory + * @work: work + * @name: directory name that is relative to share + * @mode: directory create mode + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode) +{ + struct user_namespace *user_ns; + struct path path; + struct dentry *dentry; + int err; + + dentry = ksmbd_vfs_kern_path_create(work, name, + LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY, + &path); + if (IS_ERR(dentry)) { + err = PTR_ERR(dentry); + if (err != -EEXIST) + ksmbd_debug(VFS, "path create failed for %s, err %d\n", + name, err); + return err; + } + + user_ns = mnt_user_ns(path.mnt); + mode |= S_IFDIR; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + err = vfs_mkdir(user_ns, d_inode(path.dentry), dentry, mode); +#else + err = vfs_mkdir(d_inode(path.dentry), dentry, mode); +#endif + if (err) { + goto out; + } else if (d_unhashed(dentry)) { + struct dentry *d; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) + d = lookup_one(user_ns, dentry->d_name.name, dentry->d_parent, + dentry->d_name.len); +#else + d = lookup_one_len(dentry->d_name.name, dentry->d_parent, + dentry->d_name.len); +#endif + if (IS_ERR(d)) { + err = PTR_ERR(d); + goto out; + } + if (unlikely(d_is_negative(d))) { + dput(d); + err = -ENOENT; + goto out; + } + + ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(d)); + dput(d); + } +out: + done_path_create(&path, dentry); + if (err) + pr_err("mkdir(%s): creation failed (err:%d)\n", name, err); + return err; +} + +static ssize_t ksmbd_vfs_getcasexattr(struct user_namespace *user_ns, + struct dentry *dentry, char *attr_name, + int attr_name_len, char **attr_value) +{ + char *name, *xattr_list = NULL; + ssize_t value_len = -ENOENT, xattr_list_len; + + xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); + if (xattr_list_len <= 0) + goto out; + + for (name = xattr_list; name - xattr_list < xattr_list_len; + name += strlen(name) + 1) { + ksmbd_debug(VFS, "%s, len %zd\n", name, strlen(name)); + if (strncasecmp(attr_name, name, attr_name_len)) + continue; + + value_len = ksmbd_vfs_getxattr(user_ns, + dentry, + name, + attr_value); + if (value_len < 0) + pr_err("failed to get xattr in file\n"); + break; + } + +out: + kvfree(xattr_list); + return value_len; +} + +static int ksmbd_vfs_stream_read(struct ksmbd_file *fp, char *buf, loff_t *pos, + size_t count) +{ + ssize_t v_len; + char *stream_buf = NULL; + + ksmbd_debug(VFS, "read stream data pos : %llu, count : %zd\n", + *pos, count); + + v_len = ksmbd_vfs_getcasexattr(file_mnt_user_ns(fp->filp), + fp->filp->f_path.dentry, + fp->stream.name, + fp->stream.size, + &stream_buf); + if ((int)v_len <= 0) + return (int)v_len; + + if (v_len <= *pos) { + count = -EINVAL; + goto free_buf; + } + + if (v_len - *pos < count) + count = v_len - *pos; + + memcpy(buf, &stream_buf[*pos], count); + +free_buf: + kvfree(stream_buf); + return count; +} + +/** + * check_lock_range() - vfs helper for smb byte range file locking + * @filp: the file to apply the lock to + * @start: lock start byte offset + * @end: lock end byte offset + * @type: byte range type read/write + * + * Return: 0 on success, otherwise error + */ +static int check_lock_range(struct file *filp, loff_t start, loff_t end, + unsigned char type) +{ + struct file_lock *flock; + struct file_lock_context *ctx = file_inode(filp)->i_flctx; + int error = 0; + + if (!ctx || list_empty_careful(&ctx->flc_posix)) + return 0; + + spin_lock(&ctx->flc_lock); + list_for_each_entry(flock, &ctx->flc_posix, fl_list) { + /* check conflict locks */ + if (flock->fl_end >= start && end >= flock->fl_start) { + if (flock->fl_type == F_RDLCK) { + if (type == WRITE) { + pr_err("not allow write by shared lock\n"); + error = 1; + goto out; + } + } else if (flock->fl_type == F_WRLCK) { + /* check owner in lock */ + if (flock->fl_file != filp) { + error = 1; + pr_err("not allow rw access by exclusive lock from other opens\n"); + goto out; + } + } + } + } +out: + spin_unlock(&ctx->flc_lock); + return error; +} + +/** + * ksmbd_vfs_read() - vfs helper for smb file read + * @work: smb work + * @fid: file id of open file + * @count: read byte count + * @pos: file pos + * + * Return: number of read bytes on success, otherwise error + */ +int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, + loff_t *pos) +{ + struct file *filp = fp->filp; + ssize_t nbytes = 0; + char *rbuf = work->aux_payload_buf; + struct inode *inode = file_inode(filp); + + if (S_ISDIR(inode->i_mode)) + return -EISDIR; + + if (unlikely(count == 0)) + return 0; + + if (work->conn->connection_type) { + if (!(fp->daccess & (FILE_READ_DATA_LE | FILE_EXECUTE_LE))) { + pr_err("no right to read(%pD)\n", fp->filp); + return -EACCES; + } + } + + if (ksmbd_stream_fd(fp)) + return ksmbd_vfs_stream_read(fp, rbuf, pos, count); + + if (!work->tcon->posix_extensions) { + int ret; + + ret = check_lock_range(filp, *pos, *pos + count - 1, READ); + if (ret) { + pr_err("unable to read due to lock\n"); + return -EAGAIN; + } + } + + nbytes = kernel_read(filp, rbuf, count, pos); + if (nbytes < 0) { + pr_err("smb read failed, err = %zd\n", nbytes); + return nbytes; + } + + filp->f_pos = *pos; + return nbytes; +} + +static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, + size_t count) +{ + char *stream_buf = NULL, *wbuf; + struct user_namespace *user_ns = file_mnt_user_ns(fp->filp); + size_t size, v_len; + int err = 0; + + ksmbd_debug(VFS, "write stream data pos : %llu, count : %zd\n", + *pos, count); + + size = *pos + count; + if (size > XATTR_SIZE_MAX) { + size = XATTR_SIZE_MAX; + count = (*pos + count) - XATTR_SIZE_MAX; + } + + v_len = ksmbd_vfs_getcasexattr(user_ns, + fp->filp->f_path.dentry, + fp->stream.name, + fp->stream.size, + &stream_buf); + if ((int)v_len < 0) { + pr_err("not found stream in xattr : %zd\n", v_len); + err = (int)v_len; + goto out; + } + + if (v_len < size) { + wbuf = kvmalloc(size, GFP_KERNEL | __GFP_ZERO); + if (!wbuf) { + err = -ENOMEM; + goto out; + } + + if (v_len > 0) + memcpy(wbuf, stream_buf, v_len); + kvfree(stream_buf); + stream_buf = wbuf; + } + + memcpy(&stream_buf[*pos], buf, count); + + err = ksmbd_vfs_setxattr(user_ns, + fp->filp->f_path.dentry, + fp->stream.name, + (void *)stream_buf, + size, + 0); + if (err < 0) + goto out; + + fp->filp->f_pos = *pos; + err = 0; +out: + kvfree(stream_buf); + return err; +} + +/** + * ksmbd_vfs_write() - vfs helper for smb file write + * @work: work + * @fid: file id of open file + * @buf: buf containing data for writing + * @count: read byte count + * @pos: file pos + * @sync: fsync after write + * @written: number of bytes written + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, + char *buf, size_t count, loff_t *pos, bool sync, + ssize_t *written) +{ + struct file *filp; + loff_t offset = *pos; + int err = 0; + + if (work->conn->connection_type) { + if (!(fp->daccess & FILE_WRITE_DATA_LE)) { + pr_err("no right to write(%pD)\n", fp->filp); + err = -EACCES; + goto out; + } + } + + filp = fp->filp; + + if (ksmbd_stream_fd(fp)) { + err = ksmbd_vfs_stream_write(fp, buf, pos, count); + if (!err) + *written = count; + goto out; + } + + if (!work->tcon->posix_extensions) { + err = check_lock_range(filp, *pos, *pos + count - 1, WRITE); + if (err) { + pr_err("unable to write due to lock\n"); + err = -EAGAIN; + goto out; + } + } + + /* Do we need to break any of a levelII oplock? */ + smb_break_all_levII_oplock(work, fp, 1); + + err = kernel_write(filp, buf, count, pos); + if (err < 0) { + ksmbd_debug(VFS, "smb write failed, err = %d\n", err); + goto out; + } + + filp->f_pos = *pos; + *written = err; + err = 0; + if (sync) { + err = vfs_fsync_range(filp, offset, offset + *written, 0); + if (err < 0) + pr_err("fsync failed for filename = %pD, err = %d\n", + fp->filp, err); + } + +out: + return err; +} + +/** + * ksmbd_vfs_getattr() - vfs helper for smb getattr + * @work: work + * @fid: file id of open file + * @attrs: inode attributes + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_getattr(const struct path *path, struct kstat *stat) +{ + int err; + + err = vfs_getattr(path, stat, STATX_BTIME, AT_STATX_SYNC_AS_STAT); + if (err) + pr_err("getattr failed, err %d\n", err); + return err; +} + +#ifdef CONFIG_SMB_INSECURE_SERVER +/** + * smb_check_attrs() - sanitize inode attributes + * @inode: inode + * @attrs: inode attributes + */ +static void smb_check_attrs(struct inode *inode, struct iattr *attrs) +{ + /* sanitize the mode change */ + if (attrs->ia_valid & ATTR_MODE) { + attrs->ia_mode &= S_IALLUGO; + attrs->ia_mode |= (inode->i_mode & ~S_IALLUGO); + } + + /* Revoke setuid/setgid on chown */ + if (!S_ISDIR(inode->i_mode) && + (((attrs->ia_valid & ATTR_UID) && + !uid_eq(attrs->ia_uid, inode->i_uid)) || + ((attrs->ia_valid & ATTR_GID) && + !gid_eq(attrs->ia_gid, inode->i_gid)))) { + attrs->ia_valid |= ATTR_KILL_PRIV; + if (attrs->ia_valid & ATTR_MODE) { + /* we're setting mode too, just clear the s*id bits */ + attrs->ia_mode &= ~S_ISUID; + if (attrs->ia_mode & 0010) + attrs->ia_mode &= ~S_ISGID; + } else { + /* set ATTR_KILL_* bits and let VFS handle it */ + attrs->ia_valid |= (ATTR_KILL_SUID | ATTR_KILL_SGID); + } + } +} + +/** + * ksmbd_vfs_setattr() - vfs helper for smb setattr + * @work: work + * @name: file name + * @fid: file id of open file + * @attrs: inode attributes + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_setattr(struct ksmbd_work *work, const char *name, u64 fid, + struct iattr *attrs) +{ + struct file *filp; + struct dentry *dentry; + struct inode *inode; + struct path path; + bool update_size = false; + int err = 0; + struct ksmbd_file *fp = NULL; + struct user_namespace *user_ns; + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + + if (name) { + err = kern_path(name, 0, &path); + if (err) { + ksmbd_revert_fsids(work); + ksmbd_debug(VFS, "lookup failed for %s, err = %d\n", + name, err); + return -ENOENT; + } + dentry = path.dentry; + inode = d_inode(dentry); + user_ns = mnt_user_ns(path.mnt); + } else { + fp = ksmbd_lookup_fd_fast(work, fid); + if (!fp) { + ksmbd_revert_fsids(work); + pr_err("failed to get filp for fid %llu\n", fid); + return -ENOENT; + } + + filp = fp->filp; + dentry = filp->f_path.dentry; + inode = d_inode(dentry); + user_ns = file_mnt_user_ns(filp); + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + err = inode_permission(user_ns, d_inode(dentry), MAY_WRITE); +#else + err = inode_permission(d_inode(dentry), MAY_WRITE); +#endif + if (err) + goto out; + + /* no need to update mode of symlink */ + if (S_ISLNK(inode->i_mode)) + attrs->ia_valid &= ~ATTR_MODE; + + /* skip setattr, if nothing to update */ + if (!attrs->ia_valid) { + err = 0; + goto out; + } + + smb_check_attrs(inode, attrs); + if (attrs->ia_valid & ATTR_SIZE) { + err = get_write_access(inode); + if (err) + goto out; + update_size = true; + } + + attrs->ia_valid |= ATTR_CTIME; + + inode_lock(inode); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + err = notify_change(user_ns, dentry, attrs, NULL); +#else + err = notify_change(dentry, attrs, NULL); +#endif + inode_unlock(inode); + + if (update_size) + put_write_access(inode); + + if (!err) { + sync_inode_metadata(inode, 1); + ksmbd_debug(VFS, "fid %llu, setattr done\n", fid); + } + +out: + if (name) + path_put(&path); + ksmbd_fd_put(work, fp); + ksmbd_revert_fsids(work); + return err; +} + +/** + * ksmbd_vfs_symlink() - vfs helper for creating smb symlink + * @name: source file name + * @symname: symlink name + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_symlink(struct ksmbd_work *work, const char *name, + const char *symname) +{ + struct path path; + struct dentry *dentry; + int err; + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + + dentry = kern_path_create(AT_FDCWD, symname, &path, 0); + if (IS_ERR(dentry)) { + ksmbd_revert_fsids(work); + err = PTR_ERR(dentry); + pr_err("path create failed for %s, err %d\n", name, err); + return err; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + err = vfs_symlink(mnt_user_ns(path.mnt), d_inode(dentry->d_parent), dentry, name); +#else + err = vfs_symlink(d_inode(dentry->d_parent), dentry, name); +#endif + if (err && (err != -EEXIST || err != -ENOSPC)) + ksmbd_debug(VFS, "failed to create symlink, err %d\n", err); + + done_path_create(&path, dentry); + ksmbd_revert_fsids(work); + return err; +} + +/** + * ksmbd_vfs_readlink() - vfs helper for reading value of symlink + * @path: path of symlink + * @buf: destination buffer for symlink value + * @lenp: destination buffer length + * + * Return: symlink value length on success, otherwise error + */ +int ksmbd_vfs_readlink(struct path *path, char *buf, int lenp) +{ + struct inode *inode; + int err; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + const char *link; + DEFINE_DELAYED_CALL(done); + int len; +#else + mm_segment_t old_fs; +#endif + + if (!path) + return -ENOENT; + + inode = d_inode(path->dentry); + if (!S_ISLNK(inode->i_mode)) + return -EINVAL; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + link = vfs_get_link(path->dentry, &done); + if (IS_ERR(link)) { + err = PTR_ERR(link); + pr_err("readlink failed, err = %d\n", err); + return err; + } + + len = strlen(link); + if (len > lenp) + len = lenp; + + memcpy(buf, link, len); + do_delayed_call(&done); + + return 0; +#else + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = inode->i_op->readlink(path->dentry, (char __user *)buf, lenp); + set_fs(old_fs); + if (err < 0) + pr_err("readlink failed, err = %d\n", err); + + return err; +#endif +} + +int ksmbd_vfs_readdir_name(struct ksmbd_work *work, + struct user_namespace *user_ns, + struct ksmbd_kstat *ksmbd_kstat, + const char *de_name, int de_name_len, + const char *dir_path) +{ + struct path path; + int rc, file_pathlen, dir_pathlen; + char *name; + + dir_pathlen = strlen(dir_path); + /* 1 for '/'*/ + file_pathlen = dir_pathlen + de_name_len + 1; + name = kmalloc(file_pathlen + 1, GFP_KERNEL); + if (!name) + return -ENOMEM; + + memcpy(name, dir_path, dir_pathlen); + memset(name + dir_pathlen, '/', 1); + memcpy(name + dir_pathlen + 1, de_name, de_name_len); + name[file_pathlen] = '\0'; + + rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, 1); + if (rc) { + pr_err("lookup failed: %s [%d]\n", name, rc); + kfree(name); + return -ENOMEM; + } + + ksmbd_vfs_fill_dentry_attrs(work, user_ns, path.dentry, ksmbd_kstat); + path_put(&path); + kfree(name); + return 0; +} +#endif + +/** + * ksmbd_vfs_fsync() - vfs helper for smb fsync + * @work: work + * @fid: file id of open file + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id) +{ + struct ksmbd_file *fp; + int err; + + fp = ksmbd_lookup_fd_slow(work, fid, p_id); + if (!fp) { + pr_err("failed to get filp for fid %llu\n", fid); + return -ENOENT; + } + err = vfs_fsync(fp->filp, 0); + if (err < 0) + pr_err("smb fsync failed, err = %d\n", err); + ksmbd_fd_put(work, fp); + return err; +} + +/** + * ksmbd_vfs_remove_file() - vfs helper for smb rmdir or unlink + * @name: directory or file name that is relative to share + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name) +{ + struct user_namespace *user_ns; + struct path path; + struct dentry *parent; + int err; + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + + err = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, false); + if (err) { + ksmbd_debug(VFS, "can't get %s, err %d\n", name, err); + ksmbd_revert_fsids(work); + return err; + } + + user_ns = mnt_user_ns(path.mnt); + parent = dget_parent(path.dentry); + err = ksmbd_vfs_lock_parent(user_ns, parent, path.dentry); + if (err) { + dput(parent); + path_put(&path); + ksmbd_revert_fsids(work); + return err; + } + + if (!d_inode(path.dentry)->i_nlink) { + err = -ENOENT; + goto out_err; + } + + if (S_ISDIR(d_inode(path.dentry)->i_mode)) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + err = vfs_rmdir(user_ns, d_inode(parent), path.dentry); +#else + err = vfs_rmdir(d_inode(parent), path.dentry); +#endif + if (err && err != -ENOTEMPTY) + ksmbd_debug(VFS, "%s: rmdir failed, err %d\n", name, + err); + } else { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + err = vfs_unlink(user_ns, d_inode(parent), path.dentry, NULL); +#else + err = vfs_unlink(d_inode(parent), path.dentry, NULL); +#endif + if (err) + ksmbd_debug(VFS, "%s: unlink failed, err %d\n", name, + err); + } + +out_err: + inode_unlock(d_inode(parent)); + dput(parent); + path_put(&path); + ksmbd_revert_fsids(work); + return err; +} + +/** + * ksmbd_vfs_link() - vfs helper for creating smb hardlink + * @oldname: source file name + * @newname: hardlink name that is relative to share + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname, + const char *newname) +{ + struct path oldpath, newpath; + struct dentry *dentry; + int err; + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + + err = kern_path(oldname, LOOKUP_NO_SYMLINKS, &oldpath); + if (err) { + pr_err("cannot get linux path for %s, err = %d\n", + oldname, err); + goto out1; + } + + dentry = ksmbd_vfs_kern_path_create(work, newname, + LOOKUP_NO_SYMLINKS | LOOKUP_REVAL, + &newpath); + if (IS_ERR(dentry)) { + err = PTR_ERR(dentry); + pr_err("path create err for %s, err %d\n", newname, err); + goto out2; + } + + err = -EXDEV; + if (oldpath.mnt != newpath.mnt) { + pr_err("vfs_link failed err %d\n", err); + goto out3; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + err = vfs_link(oldpath.dentry, mnt_user_ns(newpath.mnt), + d_inode(newpath.dentry), + dentry, NULL); +#else + err = vfs_link(oldpath.dentry, d_inode(newpath.dentry), dentry, NULL); +#endif + if (err) + ksmbd_debug(VFS, "vfs_link failed err %d\n", err); + +out3: + done_path_create(&newpath, dentry); +out2: + path_put(&oldpath); +out1: + ksmbd_revert_fsids(work); + return err; +} + +static int ksmbd_validate_entry_in_use(struct dentry *src_dent) +{ + struct dentry *dst_dent; + + spin_lock(&src_dent->d_lock); + list_for_each_entry(dst_dent, &src_dent->d_subdirs, d_child) { + struct ksmbd_file *child_fp; + + if (d_really_is_negative(dst_dent)) + continue; + + child_fp = ksmbd_lookup_fd_inode(d_inode(dst_dent)); + if (child_fp) { + spin_unlock(&src_dent->d_lock); + ksmbd_debug(VFS, "Forbid rename, sub file/dir is in use\n"); + return -EACCES; + } + } + spin_unlock(&src_dent->d_lock); + + return 0; +} + +static int __ksmbd_vfs_rename(struct ksmbd_work *work, + struct user_namespace *src_user_ns, + struct dentry *src_dent_parent, + struct dentry *src_dent, + struct user_namespace *dst_user_ns, + struct dentry *dst_dent_parent, + struct dentry *trap_dent, + char *dst_name) +{ + struct dentry *dst_dent; + int err; + + if (!work->tcon->posix_extensions) { + err = ksmbd_validate_entry_in_use(src_dent); + if (err) + return err; + } + + if (d_really_is_negative(src_dent_parent)) + return -ENOENT; + if (d_really_is_negative(dst_dent_parent)) + return -ENOENT; + if (d_really_is_negative(src_dent)) + return -ENOENT; + if (src_dent == trap_dent) + return -EINVAL; + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) + dst_dent = lookup_one(dst_user_ns, dst_name, dst_dent_parent, + strlen(dst_name)); +#else + dst_dent = lookup_one_len(dst_name, dst_dent_parent, + strlen(dst_name)); +#endif + err = PTR_ERR(dst_dent); + if (IS_ERR(dst_dent)) { + pr_err("lookup failed %s [%d]\n", dst_name, err); + goto out; + } + + err = -ENOTEMPTY; + if (dst_dent != trap_dent && !d_really_is_positive(dst_dent)) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + struct renamedata rd = { + .old_mnt_userns = src_user_ns, + .old_dir = d_inode(src_dent_parent), + .old_dentry = src_dent, + .new_mnt_userns = dst_user_ns, + .new_dir = d_inode(dst_dent_parent), + .new_dentry = dst_dent, + }; + err = vfs_rename(&rd); +#else + err = vfs_rename(d_inode(src_dent_parent), + src_dent, + d_inode(dst_dent_parent), + dst_dent, + NULL, + 0); +#endif + } + if (err) + pr_err("vfs_rename failed err %d\n", err); + if (dst_dent) + dput(dst_dent); +out: + ksmbd_revert_fsids(work); + return err; +} + +int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, + char *newname) +{ + struct user_namespace *user_ns; + struct path dst_path; + struct dentry *src_dent_parent, *dst_dent_parent; + struct dentry *src_dent, *trap_dent, *src_child; + char *dst_name; + int err; + + dst_name = extract_last_component(newname); + if (!dst_name) { + dst_name = newname; + newname = ""; + } + + src_dent_parent = dget_parent(fp->filp->f_path.dentry); + src_dent = fp->filp->f_path.dentry; + + err = ksmbd_vfs_kern_path(work, newname, + LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY, + &dst_path, false); + if (err) { + ksmbd_debug(VFS, "Cannot get path for %s [%d]\n", newname, err); + goto out; + } + dst_dent_parent = dst_path.dentry; + + trap_dent = lock_rename(src_dent_parent, dst_dent_parent); + dget(src_dent); + dget(dst_dent_parent); + user_ns = file_mnt_user_ns(fp->filp); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) + src_child = lookup_one(user_ns, src_dent->d_name.name, src_dent_parent, + src_dent->d_name.len); +#else + src_child = lookup_one_len(src_dent->d_name.name, src_dent_parent, + src_dent->d_name.len); +#endif + if (IS_ERR(src_child)) { + err = PTR_ERR(src_child); + goto out_lock; + } + + if (src_child != src_dent) { + err = -ESTALE; + dput(src_child); + goto out_lock; + } + dput(src_child); + + err = __ksmbd_vfs_rename(work, + user_ns, + src_dent_parent, + src_dent, + mnt_user_ns(dst_path.mnt), + dst_dent_parent, + trap_dent, + dst_name); +out_lock: + dput(src_dent); + dput(dst_dent_parent); + unlock_rename(src_dent_parent, dst_dent_parent); + path_put(&dst_path); +out: + dput(src_dent_parent); + return err; +} + +/** + * ksmbd_vfs_truncate() - vfs helper for smb file truncate + * @work: work + * @name: old filename + * @fid: file id of old file + * @size: truncate to given size + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_truncate(struct ksmbd_work *work, + struct ksmbd_file *fp, loff_t size) +{ + int err = 0; + struct file *filp; + + if (size < 0) + return -EINVAL; + + filp = fp->filp; + + /* Do we need to break any of a levelII oplock? */ + smb_break_all_levII_oplock(work, fp, 1); + + if (!work->tcon->posix_extensions) { + struct inode *inode = file_inode(filp); + + if (size < inode->i_size) { + err = check_lock_range(filp, size, + inode->i_size - 1, WRITE); + } else { + err = check_lock_range(filp, inode->i_size, + size - 1, WRITE); + } + + if (err) { + pr_err("failed due to lock\n"); + return -EAGAIN; + } + } + + err = vfs_truncate(&filp->f_path, size); + if (err) + pr_err("truncate failed, err %d\n", err); + return err; +} + +/** + * ksmbd_vfs_listxattr() - vfs helper for smb list extended attributes + * @dentry: dentry of file for listing xattrs + * @list: destination buffer + * @size: destination buffer length + * + * Return: xattr list length on success, otherwise error + */ +ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list) +{ + ssize_t size; + char *vlist = NULL; + + size = vfs_listxattr(dentry, NULL, 0); + if (size <= 0) + return size; + + vlist = kvmalloc(size, GFP_KERNEL | __GFP_ZERO); + if (!vlist) + return -ENOMEM; + + *list = vlist; + size = vfs_listxattr(dentry, vlist, size); + if (size < 0) { + ksmbd_debug(VFS, "listxattr failed\n"); + kvfree(vlist); + *list = NULL; + } + + return size; +} + +static ssize_t ksmbd_vfs_xattr_len(struct user_namespace *user_ns, + struct dentry *dentry, char *xattr_name) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + return vfs_getxattr(user_ns, dentry, xattr_name, NULL, 0); +#else + return vfs_getxattr(dentry, xattr_name, NULL, 0); +#endif +} + +/** + * ksmbd_vfs_getxattr() - vfs helper for smb get extended attributes value ++ @user_ns: user namespace + * @dentry: dentry of file for getting xattrs + * @xattr_name: name of xattr name to query + * @xattr_buf: destination buffer xattr value + * + * Return: read xattr value length on success, otherwise error + */ +ssize_t ksmbd_vfs_getxattr(struct user_namespace *user_ns, + struct dentry *dentry, + char *xattr_name, char **xattr_buf) +{ + ssize_t xattr_len; + char *buf; + + *xattr_buf = NULL; + xattr_len = ksmbd_vfs_xattr_len(user_ns, dentry, xattr_name); + if (xattr_len < 0) + return xattr_len; + + buf = kmalloc(xattr_len + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + xattr_len = vfs_getxattr(user_ns, dentry, xattr_name, + (void *)buf, xattr_len); +#else + xattr_len = vfs_getxattr(dentry, xattr_name, (void *)buf, xattr_len); +#endif + if (xattr_len > 0) + *xattr_buf = buf; + else + kfree(buf); + return xattr_len; +} + +/** + * ksmbd_vfs_setxattr() - vfs helper for smb set extended attributes value + * @user_ns: user namespace + * @dentry: dentry to set XATTR at + * @name: xattr name for setxattr + * @value: xattr value to set + * @size: size of xattr value + * @flags: destination buffer length + * + * Return: 0 on success, otherwise error + */ +int ksmbd_vfs_setxattr(struct user_namespace *user_ns, + struct dentry *dentry, const char *attr_name, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + void *attr_value, size_t attr_size, int flags) +#else + const void *attr_value, size_t attr_size, int flags) +#endif +{ + int err; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + err = vfs_setxattr(user_ns, + dentry, +#else + err = vfs_setxattr(dentry, +#endif + attr_name, + attr_value, + attr_size, + flags); + if (err) + ksmbd_debug(VFS, "setxattr failed, err %d\n", err); + return err; +} + +#ifdef CONFIG_SMB_INSECURE_SERVER +int ksmbd_vfs_fsetxattr(struct ksmbd_work *work, const char *filename, + const char *attr_name, const void *attr_value, + size_t attr_size, int flags) +{ + struct path path; + int err; + + if (ksmbd_override_fsids(work)) + return -ENOMEM; + + err = kern_path(filename, 0, &path); + if (err) { + ksmbd_revert_fsids(work); + ksmbd_debug(VFS, "cannot get linux path %s, err %d\n", + filename, err); + return err; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + err = vfs_setxattr(mnt_user_ns(path.mnt), path.dentry, +#else + err = vfs_setxattr(path.dentry, +#endif + attr_name, + attr_value, + attr_size, + flags); + if (err) + ksmbd_debug(VFS, "setxattr failed, err %d\n", err); + path_put(&path); + ksmbd_revert_fsids(work); + return err; +} +#endif + +struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, + const char *name, + unsigned int flags, + struct path *path) +{ + char *abs_name; + struct dentry *dent; + + abs_name = convert_to_unix_name(work->tcon->share_conf, name); + if (!abs_name) + return ERR_PTR(-ENOMEM); + + dent = kern_path_create(AT_FDCWD, abs_name, path, flags); + kfree(abs_name); + return dent; +} + +int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns, + struct dentry *dentry) +{ + char *name, *xattr_list = NULL; + ssize_t xattr_list_len; + int err = 0; + + xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); + if (xattr_list_len < 0) { + goto out; + } else if (!xattr_list_len) { + ksmbd_debug(SMB, "empty xattr in the file\n"); + goto out; + } + + for (name = xattr_list; name - xattr_list < xattr_list_len; + name += strlen(name) + 1) { + ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); + + if (!strncmp(name, XATTR_NAME_POSIX_ACL_ACCESS, + sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1) || + !strncmp(name, XATTR_NAME_POSIX_ACL_DEFAULT, + sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1)) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 2, 0) + err = vfs_remove_acl(user_ns, dentry, name); +#else + err = ksmbd_vfs_remove_xattr(user_ns, dentry, name); +#endif + if (err) + ksmbd_debug(SMB, + "remove acl xattr failed : %s\n", name); + } + } +out: + kvfree(xattr_list); + return err; +} + +int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, + struct dentry *dentry) +{ + char *name, *xattr_list = NULL; + ssize_t xattr_list_len; + int err = 0; + + xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); + if (xattr_list_len < 0) { + goto out; + } else if (!xattr_list_len) { + ksmbd_debug(SMB, "empty xattr in the file\n"); + goto out; + } + + for (name = xattr_list; name - xattr_list < xattr_list_len; + name += strlen(name) + 1) { + ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); + + if (!strncmp(name, XATTR_NAME_SD, XATTR_NAME_SD_LEN)) { + err = ksmbd_vfs_remove_xattr(user_ns, dentry, name); + if (err) + ksmbd_debug(SMB, "remove xattr failed : %s\n", name); + } + } +out: + kvfree(xattr_list); + return err; +} + +static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct user_namespace *user_ns, + struct inode *inode, + int acl_type) +{ + struct xattr_smb_acl *smb_acl = NULL; + struct posix_acl *posix_acls; + struct posix_acl_entry *pa_entry; + struct xattr_acl_entry *xa_entry; + int i; + + if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) + return NULL; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 2, 0) + posix_acls = get_inode_acl(inode, acl_type); +#else + posix_acls = get_acl(inode, acl_type); +#endif + if (!posix_acls) + return NULL; + + smb_acl = kzalloc(sizeof(struct xattr_smb_acl) + + sizeof(struct xattr_acl_entry) * posix_acls->a_count, + GFP_KERNEL); + if (!smb_acl) + goto out; + + smb_acl->count = posix_acls->a_count; + pa_entry = posix_acls->a_entries; + xa_entry = smb_acl->entries; + for (i = 0; i < posix_acls->a_count; i++, pa_entry++, xa_entry++) { + switch (pa_entry->e_tag) { + case ACL_USER: + xa_entry->type = SMB_ACL_USER; + xa_entry->uid = posix_acl_uid_translate(user_ns, pa_entry); + break; + case ACL_USER_OBJ: + xa_entry->type = SMB_ACL_USER_OBJ; + break; + case ACL_GROUP: + xa_entry->type = SMB_ACL_GROUP; + xa_entry->gid = posix_acl_gid_translate(user_ns, pa_entry); + break; + case ACL_GROUP_OBJ: + xa_entry->type = SMB_ACL_GROUP_OBJ; + break; + case ACL_OTHER: + xa_entry->type = SMB_ACL_OTHER; + break; + case ACL_MASK: + xa_entry->type = SMB_ACL_MASK; + break; + default: + pr_err("unknown type : 0x%x\n", pa_entry->e_tag); + goto out; + } + + if (pa_entry->e_perm & ACL_READ) + xa_entry->perm |= SMB_ACL_READ; + if (pa_entry->e_perm & ACL_WRITE) + xa_entry->perm |= SMB_ACL_WRITE; + if (pa_entry->e_perm & ACL_EXECUTE) + xa_entry->perm |= SMB_ACL_EXECUTE; + } +out: + posix_acl_release(posix_acls); + return smb_acl; +} + +int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, + struct dentry *dentry, + struct smb_ntsd *pntsd, int len) +{ + int rc; + struct ndr sd_ndr = {0}, acl_ndr = {0}; + struct xattr_ntacl acl = {0}; + struct xattr_smb_acl *smb_acl, *def_smb_acl = NULL; + struct inode *inode = d_inode(dentry); + + acl.version = 4; + acl.hash_type = XATTR_SD_HASH_TYPE_SHA256; + acl.current_time = ksmbd_UnixTimeToNT(current_time(inode)); + + memcpy(acl.desc, "posix_acl", 9); + acl.desc_len = 10; + + pntsd->osidoffset = + cpu_to_le32(le32_to_cpu(pntsd->osidoffset) + NDR_NTSD_OFFSETOF); + pntsd->gsidoffset = + cpu_to_le32(le32_to_cpu(pntsd->gsidoffset) + NDR_NTSD_OFFSETOF); + pntsd->dacloffset = + cpu_to_le32(le32_to_cpu(pntsd->dacloffset) + NDR_NTSD_OFFSETOF); + + acl.sd_buf = (char *)pntsd; + acl.sd_size = len; + + rc = ksmbd_gen_sd_hash(conn, acl.sd_buf, acl.sd_size, acl.hash); + if (rc) { + pr_err("failed to generate hash for ndr acl\n"); + return rc; + } + + smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, + ACL_TYPE_ACCESS); + if (S_ISDIR(inode->i_mode)) + def_smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, + ACL_TYPE_DEFAULT); + + rc = ndr_encode_posix_acl(&acl_ndr, user_ns, inode, + smb_acl, def_smb_acl); + if (rc) { + pr_err("failed to encode ndr to posix acl\n"); + goto out; + } + + rc = ksmbd_gen_sd_hash(conn, acl_ndr.data, acl_ndr.offset, + acl.posix_acl_hash); + if (rc) { + pr_err("failed to generate hash for ndr acl\n"); + goto out; + } + + rc = ndr_encode_v4_ntacl(&sd_ndr, &acl); + if (rc) { + pr_err("failed to encode ndr to posix acl\n"); + goto out; + } + + rc = ksmbd_vfs_setxattr(user_ns, dentry, + XATTR_NAME_SD, sd_ndr.data, + sd_ndr.offset, 0); + if (rc < 0) + pr_err("Failed to store XATTR ntacl :%d\n", rc); + + kfree(sd_ndr.data); +out: + kfree(acl_ndr.data); + kfree(smb_acl); + kfree(def_smb_acl); + return rc; +} + +int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, + struct dentry *dentry, + struct smb_ntsd **pntsd) +{ + int rc; + struct ndr n; + struct inode *inode = d_inode(dentry); + struct ndr acl_ndr = {0}; + struct xattr_ntacl acl; + struct xattr_smb_acl *smb_acl = NULL, *def_smb_acl = NULL; + __u8 cmp_hash[XATTR_SD_HASH_SIZE] = {0}; + + rc = ksmbd_vfs_getxattr(user_ns, dentry, XATTR_NAME_SD, &n.data); + if (rc <= 0) + return rc; + + n.length = rc; + rc = ndr_decode_v4_ntacl(&n, &acl); + if (rc) + goto free_n_data; + + smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, + ACL_TYPE_ACCESS); + if (S_ISDIR(inode->i_mode)) + def_smb_acl = ksmbd_vfs_make_xattr_posix_acl(user_ns, inode, + ACL_TYPE_DEFAULT); + + rc = ndr_encode_posix_acl(&acl_ndr, user_ns, inode, smb_acl, + def_smb_acl); + if (rc) { + pr_err("failed to encode ndr to posix acl\n"); + goto out_free; + } + + rc = ksmbd_gen_sd_hash(conn, acl_ndr.data, acl_ndr.offset, cmp_hash); + if (rc) { + pr_err("failed to generate hash for ndr acl\n"); + goto out_free; + } + + if (memcmp(cmp_hash, acl.posix_acl_hash, XATTR_SD_HASH_SIZE)) { + pr_err("hash value diff\n"); + rc = -EINVAL; + goto out_free; + } + + *pntsd = acl.sd_buf; + if (acl.sd_size < sizeof(struct smb_ntsd)) { + pr_err("sd size is invalid\n"); + goto out_free; + } + + (*pntsd)->osidoffset = cpu_to_le32(le32_to_cpu((*pntsd)->osidoffset) - + NDR_NTSD_OFFSETOF); + (*pntsd)->gsidoffset = cpu_to_le32(le32_to_cpu((*pntsd)->gsidoffset) - + NDR_NTSD_OFFSETOF); + (*pntsd)->dacloffset = cpu_to_le32(le32_to_cpu((*pntsd)->dacloffset) - + NDR_NTSD_OFFSETOF); + + rc = acl.sd_size; +out_free: + kfree(acl_ndr.data); + kfree(smb_acl); + kfree(def_smb_acl); + if (rc < 0) { + kfree(acl.sd_buf); + *pntsd = NULL; + } + +free_n_data: + kfree(n.data); + return rc; +} + +int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns, + struct dentry *dentry, + struct xattr_dos_attrib *da) +{ + struct ndr n; + int err; + + err = ndr_encode_dos_attr(&n, da); + if (err) + return err; + + err = ksmbd_vfs_setxattr(user_ns, dentry, XATTR_NAME_DOS_ATTRIBUTE, + (void *)n.data, n.offset, 0); + if (err) + ksmbd_debug(SMB, "failed to store dos attribute in xattr\n"); + kfree(n.data); + + return err; +} + +int ksmbd_vfs_get_dos_attrib_xattr(struct user_namespace *user_ns, + struct dentry *dentry, + struct xattr_dos_attrib *da) +{ + struct ndr n; + int err; + + err = ksmbd_vfs_getxattr(user_ns, dentry, XATTR_NAME_DOS_ATTRIBUTE, + (char **)&n.data); + if (err > 0) { + n.length = err; + if (ndr_decode_dos_attr(&n, da)) + err = -EINVAL; + kfree(n.data); + } else { + ksmbd_debug(SMB, "failed to load dos attribute in xattr\n"); + } + + return err; +} + +/** + * ksmbd_vfs_set_fadvise() - convert smb IO caching options to linux options + * @filp: file pointer for IO + * @options: smb IO options + */ +void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option) +{ + struct address_space *mapping; + + mapping = filp->f_mapping; + + if (!option || !mapping) + return; + + if (option & FILE_WRITE_THROUGH_LE) { + filp->f_flags |= O_SYNC; + } else if (option & FILE_SEQUENTIAL_ONLY_LE) { + filp->f_ra.ra_pages = inode_to_bdi(mapping->host)->ra_pages * 2; + spin_lock(&filp->f_lock); + filp->f_mode &= ~FMODE_RANDOM; + spin_unlock(&filp->f_lock); + } else if (option & FILE_RANDOM_ACCESS_LE) { + spin_lock(&filp->f_lock); + filp->f_mode |= FMODE_RANDOM; + spin_unlock(&filp->f_lock); + } +} + +int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, + loff_t off, loff_t len) +{ + smb_break_all_levII_oplock(work, fp, 1); + if (fp->f_ci->m_fattr & ATTR_SPARSE_FILE_LE) + return vfs_fallocate(fp->filp, + FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, + off, len); + + return vfs_fallocate(fp->filp, + FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE, + off, len); +} + +int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, + struct file_allocated_range_buffer *ranges, + unsigned int in_count, unsigned int *out_count) +{ + struct file *f = fp->filp; + struct inode *inode = file_inode(fp->filp); + loff_t maxbytes = (u64)inode->i_sb->s_maxbytes, end; + loff_t extent_start, extent_end; + int ret = 0; + + if (start > maxbytes) + return -EFBIG; + + if (!in_count) + return 0; + + /* + * Shrink request scope to what the fs can actually handle. + */ + if (length > maxbytes || (maxbytes - length) < start) + length = maxbytes - start; + + if (start + length > inode->i_size) + length = inode->i_size - start; + + *out_count = 0; + end = start + length; + while (start < end && *out_count < in_count) { + extent_start = vfs_llseek(f, start, SEEK_DATA); + if (extent_start < 0) { + if (extent_start != -ENXIO) + ret = (int)extent_start; + break; + } + + if (extent_start >= end) + break; + + extent_end = vfs_llseek(f, extent_start, SEEK_HOLE); + if (extent_end < 0) { + if (extent_end != -ENXIO) + ret = (int)extent_end; + break; + } else if (extent_start >= extent_end) { + break; + } + + ranges[*out_count].file_offset = cpu_to_le64(extent_start); + ranges[(*out_count)++].length = + cpu_to_le64(min(extent_end, end) - extent_start); + + start = extent_end; + } + + return ret; +} + +int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns, + struct dentry *dentry, char *attr_name) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + return vfs_removexattr(user_ns, dentry, attr_name); +#else + return vfs_removexattr(dentry, attr_name); +#endif +} + +int ksmbd_vfs_unlink(struct user_namespace *user_ns, + struct dentry *dir, struct dentry *dentry) +{ + int err = 0; + + err = ksmbd_vfs_lock_parent(user_ns, dir, dentry); + if (err) + return err; + + dget(dentry); + if (S_ISDIR(d_inode(dentry)->i_mode)) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + err = vfs_rmdir(user_ns, d_inode(dir), dentry); + else + err = vfs_unlink(user_ns, d_inode(dir), dentry, NULL); +#else + err = vfs_rmdir(d_inode(dir), dentry); + else + err = vfs_unlink(d_inode(dir), dentry, NULL); +#endif + + dput(dentry); + inode_unlock(d_inode(dir)); + if (err) + ksmbd_debug(VFS, "failed to delete, err %d\n", err); + + return err; +} + +#ifdef CONFIG_SMB_INSECURE_SERVER +/** + * ksmbd_vfs_dentry_open() - open a dentry and provide fid for it + * @work: smb work ptr + * @path: path of dentry to be opened + * @flags: open flags + * @ret_id: fid returned on this + * @option: file access pattern options for fadvise + * @fexist: file already present or not + * + * Return: allocated struct ksmbd_file on success, otherwise error pointer + */ +struct ksmbd_file *ksmbd_vfs_dentry_open(struct ksmbd_work *work, + const struct path *path, int flags, + __le32 option, int fexist) +{ + struct file *filp; + int err = 0; + struct ksmbd_file *fp = NULL; + + filp = dentry_open(path, flags | O_LARGEFILE, current_cred()); + if (IS_ERR(filp)) { + err = PTR_ERR(filp); + pr_err("dentry open failed, err %d\n", err); + return ERR_PTR(err); + } + + ksmbd_vfs_set_fadvise(filp, option); + + fp = ksmbd_open_fd(work, filp); + if (IS_ERR(fp)) { + fput(filp); + err = PTR_ERR(fp); + pr_err("id insert failed\n"); + goto err_out; + } + + if (flags & O_TRUNC) { + if (fexist) + smb_break_all_oplock(work, fp); + err = vfs_truncate((struct path *)path, 0); + if (err) + goto err_out; + } + return fp; + +err_out: + if (!IS_ERR(fp)) + ksmbd_close_fd(work, fp->volatile_id); + if (err) { + fp = ERR_PTR(err); + pr_err("err : %d\n", err); + } + return fp; +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) +static bool __dir_empty(struct dir_context *ctx, const char *name, int namlen, +#else +static int __dir_empty(struct dir_context *ctx, const char *name, int namlen, +#endif + loff_t offset, u64 ino, unsigned int d_type) +{ + struct ksmbd_readdir_data *buf; + + buf = container_of(ctx, struct ksmbd_readdir_data, ctx); + buf->dirent_count++; + + return buf->dirent_count <= 2; +} + +/** + * ksmbd_vfs_empty_dir() - check for empty directory + * @fp: ksmbd file pointer + * + * Return: true if directory empty, otherwise false + */ +int ksmbd_vfs_empty_dir(struct ksmbd_file *fp) +{ + int err; + struct ksmbd_readdir_data readdir_data; + + memset(&readdir_data, 0, sizeof(struct ksmbd_readdir_data)); + + set_ctx_actor(&readdir_data.ctx, __dir_empty); + readdir_data.dirent_count = 0; + + err = iterate_dir(fp->filp, &readdir_data.ctx); + if (readdir_data.dirent_count > 2) + err = -ENOTEMPTY; + else + err = 0; + return err; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) +static bool __caseless_lookup(struct dir_context *ctx, const char *name, +#else +static int __caseless_lookup(struct dir_context *ctx, const char *name, +#endif + int namlen, loff_t offset, u64 ino, + unsigned int d_type) +{ + struct ksmbd_readdir_data *buf; + int cmp = -EINVAL; + + buf = container_of(ctx, struct ksmbd_readdir_data, ctx); + + if (buf->used != namlen) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) + return true; +#else + return 0; +#endif + if (IS_ENABLED(CONFIG_UNICODE) && buf->um) { + const struct qstr q_buf = {.name = buf->private, + .len = buf->used}; + const struct qstr q_name = {.name = name, + .len = namlen}; + + cmp = utf8_strncasecmp(buf->um, &q_buf, &q_name); + } + if (cmp < 0) + cmp = strncasecmp((char *)buf->private, name, namlen); + if (!cmp) { + memcpy((char *)buf->private, name, namlen); + buf->dirent_count = 1; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) + return false; +#else + return -EEXIST; +#endif + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) + return true; +#else + return 0; +#endif +} + +/** + * ksmbd_vfs_lookup_in_dir() - lookup a file in a directory + * @dir: path info + * @name: filename to lookup + * @namelen: filename length + * + * Return: 0 on success, otherwise error + */ +static int ksmbd_vfs_lookup_in_dir(const struct path *dir, char *name, + size_t namelen, struct unicode_map *um) +{ + int ret; + struct file *dfilp; + int flags = O_RDONLY | O_LARGEFILE; + struct ksmbd_readdir_data readdir_data = { + .ctx.actor = __caseless_lookup, + .private = name, + .used = namelen, + .dirent_count = 0, + .um = um, + }; + + dfilp = dentry_open(dir, flags, current_cred()); + if (IS_ERR(dfilp)) + return PTR_ERR(dfilp); + + ret = iterate_dir(dfilp, &readdir_data.ctx); + if (readdir_data.dirent_count > 0) + ret = 0; + fput(dfilp); + return ret; +} + +/** + * ksmbd_vfs_kern_path() - lookup a file and get path info + * @name: file path that is relative to share + * @flags: lookup flags + * @path: if lookup succeed, return path info + * @caseless: caseless filename lookup + * + * Return: 0 on success, otherwise error + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name, + unsigned int flags, struct path *path, bool caseless) +{ + struct ksmbd_share_config *share_conf = work->tcon->share_conf; + int err; + + flags |= LOOKUP_BENEATH; + err = vfs_path_lookup(share_conf->vfs_path.dentry, + share_conf->vfs_path.mnt, + name, + flags, + path); + if (!err) + return 0; + + if (caseless) { + char *filepath; + struct path parent; + size_t path_len, remain_len; + + filepath = kstrdup(name, GFP_KERNEL); + if (!filepath) + return -ENOMEM; + + path_len = strlen(filepath); + remain_len = path_len; + + parent = share_conf->vfs_path; + path_get(&parent); + + while (d_can_lookup(parent.dentry)) { + char *filename = filepath + path_len - remain_len; + char *next = strchrnul(filename, '/'); + size_t filename_len = next - filename; + bool is_last = !next[0]; + + if (filename_len == 0) + break; + + err = ksmbd_vfs_lookup_in_dir(&parent, filename, + filename_len, + work->conn->um); + path_put(&parent); + if (err) + goto out; + + next[0] = '\0'; + + err = vfs_path_lookup(share_conf->vfs_path.dentry, + share_conf->vfs_path.mnt, + filepath, + flags, + &parent); + if (err) + goto out; + else if (is_last) { + *path = parent; + goto out; + } + + next[0] = '/'; + remain_len -= filename_len + 1; + } + + path_put(&parent); + err = -EINVAL; +out: + kfree(filepath); + } + return err; +} +#else +int ksmbd_vfs_kern_path(struct ksmbd_work *work, char *name, + unsigned int flags, struct path *path, bool caseless) +{ + char *abs_name; + int err; + + abs_name = convert_to_unix_name(work->tcon->share_conf, name); + if (IS_ERR(abs_name)) + return PTR_ERR(abs_name); + + err = kern_path(abs_name, flags, path); + if (!err) { + err = 0; + goto free_abs_name; + } + + if (caseless) { + char *filepath; + struct path parent; + size_t path_len, remain_len; + + filepath = kstrdup(abs_name, GFP_KERNEL); + if (!filepath) { + err = -ENOMEM; + goto free_abs_name; + } + + path_len = strlen(filepath); + remain_len = path_len - 1; + + err = kern_path("/", flags, &parent); + if (err) + goto out; + + while (d_can_lookup(parent.dentry)) { + char *filename = filepath + path_len - remain_len; + char *next = strchrnul(filename, '/'); + size_t filename_len = next - filename; + bool is_last = !next[0]; + + if (filename_len == 0) + break; + + err = ksmbd_vfs_lookup_in_dir(&parent, filename, + filename_len, + work->conn->um); + if (err) { + path_put(&parent); + goto out; + } + + path_put(&parent); + next[0] = '\0'; + + err = kern_path(filepath, flags, &parent); + if (err) + goto out; + + if (is_last) { + path->mnt = parent.mnt; + path->dentry = parent.dentry; + goto out; + } + + next[0] = '/'; + remain_len -= filename_len + 1; + } + + path_put(&parent); + err = -EINVAL; +out: + kfree(filepath); + } + +free_abs_name: + kfree(abs_name); + return err; +} +#endif + +/** + * ksmbd_vfs_init_kstat() - convert unix stat information to smb stat format + * @p: destination buffer + * @ksmbd_kstat: ksmbd kstat wrapper + */ +void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat) +{ + struct file_directory_info *info = (struct file_directory_info *)(*p); + struct kstat *kstat = ksmbd_kstat->kstat; + u64 time; + + info->FileIndex = 0; + info->CreationTime = cpu_to_le64(ksmbd_kstat->create_time); + time = ksmbd_UnixTimeToNT(kstat->atime); + info->LastAccessTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(kstat->mtime); + info->LastWriteTime = cpu_to_le64(time); + time = ksmbd_UnixTimeToNT(kstat->ctime); + info->ChangeTime = cpu_to_le64(time); + + if (ksmbd_kstat->file_attributes & ATTR_DIRECTORY_LE) { + info->EndOfFile = 0; + info->AllocationSize = 0; + } else { + info->EndOfFile = cpu_to_le64(kstat->size); + info->AllocationSize = cpu_to_le64(kstat->blocks << 9); + } + info->ExtFileAttributes = ksmbd_kstat->file_attributes; + + return info; +} + +int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, + struct user_namespace *user_ns, + struct dentry *dentry, + struct ksmbd_kstat *ksmbd_kstat) +{ + u64 time; + int rc; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + generic_fillattr(user_ns, d_inode(dentry), ksmbd_kstat->kstat); +#else + generic_fillattr(d_inode(dentry), ksmbd_kstat->kstat); +#endif + + time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->ctime); + ksmbd_kstat->create_time = time; + + /* + * set default value for the case that store dos attributes is not yes + * or that acl is disable in server's filesystem and the config is yes. + */ + if (S_ISDIR(ksmbd_kstat->kstat->mode)) + ksmbd_kstat->file_attributes = ATTR_DIRECTORY_LE; + else + ksmbd_kstat->file_attributes = ATTR_ARCHIVE_LE; + + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { + struct xattr_dos_attrib da; + + rc = ksmbd_vfs_get_dos_attrib_xattr(user_ns, dentry, &da); + if (rc > 0) { + ksmbd_kstat->file_attributes = cpu_to_le32(da.attr); + ksmbd_kstat->create_time = da.create_time; + } else { + ksmbd_debug(VFS, "fail to load dos attribute.\n"); + } + } + + return 0; +} + +ssize_t ksmbd_vfs_casexattr_len(struct user_namespace *user_ns, + struct dentry *dentry, char *attr_name, + int attr_name_len) +{ + char *name, *xattr_list = NULL; + ssize_t value_len = -ENOENT, xattr_list_len; + + xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); + if (xattr_list_len <= 0) + goto out; + + for (name = xattr_list; name - xattr_list < xattr_list_len; + name += strlen(name) + 1) { + ksmbd_debug(VFS, "%s, len %zd\n", name, strlen(name)); + if (strncasecmp(attr_name, name, attr_name_len)) + continue; + + value_len = ksmbd_vfs_xattr_len(user_ns, dentry, name); + break; + } + +out: + kvfree(xattr_list); + return value_len; +} + +int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, + size_t *xattr_stream_name_size, int s_type) +{ + char *type, *buf; + + if (s_type == DIR_STREAM) + type = ":$INDEX_ALLOCATION"; + else + type = ":$DATA"; + + buf = kasprintf(GFP_KERNEL, "%s%s%s", + XATTR_NAME_STREAM, stream_name, type); + if (!buf) + return -ENOMEM; + + *xattr_stream_name = buf; + *xattr_stream_name_size = strlen(buf) + 1; + + return 0; +} + +int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, + struct ksmbd_file *src_fp, + struct ksmbd_file *dst_fp, + struct srv_copychunk *chunks, + unsigned int chunk_count, + unsigned int *chunk_count_written, + unsigned int *chunk_size_written, + loff_t *total_size_written) +{ + unsigned int i; + loff_t src_off, dst_off, src_file_size; + size_t len; + int ret; + + *chunk_count_written = 0; + *chunk_size_written = 0; + *total_size_written = 0; + + if (!(src_fp->daccess & (FILE_READ_DATA_LE | FILE_EXECUTE_LE))) { + pr_err("no right to read(%pD)\n", src_fp->filp); + return -EACCES; + } + if (!(dst_fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE))) { + pr_err("no right to write(%pD)\n", dst_fp->filp); + return -EACCES; + } + + if (ksmbd_stream_fd(src_fp) || ksmbd_stream_fd(dst_fp)) + return -EBADF; + + smb_break_all_levII_oplock(work, dst_fp, 1); + + if (!work->tcon->posix_extensions) { + for (i = 0; i < chunk_count; i++) { + src_off = le64_to_cpu(chunks[i].SourceOffset); + dst_off = le64_to_cpu(chunks[i].TargetOffset); + len = le32_to_cpu(chunks[i].Length); + + if (check_lock_range(src_fp->filp, src_off, + src_off + len - 1, READ)) + return -EAGAIN; + if (check_lock_range(dst_fp->filp, dst_off, + dst_off + len - 1, WRITE)) + return -EAGAIN; + } + } + + src_file_size = i_size_read(file_inode(src_fp->filp)); + + for (i = 0; i < chunk_count; i++) { + src_off = le64_to_cpu(chunks[i].SourceOffset); + dst_off = le64_to_cpu(chunks[i].TargetOffset); + len = le32_to_cpu(chunks[i].Length); + + if (src_off + len > src_file_size) + return -E2BIG; + + ret = vfs_copy_file_range(src_fp->filp, src_off, + dst_fp->filp, dst_off, len, 0); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0) + if (ret == -EOPNOTSUPP || ret == -EXDEV) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) + ret = vfs_copy_file_range(src_fp->filp, src_off, + dst_fp->filp, dst_off, len, + COPY_FILE_SPLICE); +#else + ret = generic_copy_file_range(src_fp->filp, src_off, + dst_fp->filp, dst_off, + len, 0); +#endif +#endif + if (ret < 0) + return ret; + + *chunk_count_written += 1; + *total_size_written += ret; + } + return 0; +} + +void ksmbd_vfs_posix_lock_wait(struct file_lock *flock) +{ + wait_event(flock->fl_wait, !flock->fl_blocker); +} + +int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout) +{ + return wait_event_interruptible_timeout(flock->fl_wait, + !flock->fl_blocker, + timeout); +} + +void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock) +{ + locks_delete_block(flock); +} + +int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns, + struct dentry *dentry) +{ + struct posix_acl_state acl_state; + struct posix_acl *acls; + struct inode *inode = d_inode(dentry); + int rc; + + if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) + return -EOPNOTSUPP; + + ksmbd_debug(SMB, "Set posix acls\n"); + rc = init_acl_state(&acl_state, 1); + if (rc) + return rc; + + /* Set default owner group */ + acl_state.owner.allow = (inode->i_mode & 0700) >> 6; + acl_state.group.allow = (inode->i_mode & 0070) >> 3; + acl_state.other.allow = inode->i_mode & 0007; + acl_state.users->aces[acl_state.users->n].uid = inode->i_uid; + acl_state.users->aces[acl_state.users->n++].perms.allow = + acl_state.owner.allow; + acl_state.groups->aces[acl_state.groups->n].gid = inode->i_gid; + acl_state.groups->aces[acl_state.groups->n++].perms.allow = + acl_state.group.allow; + acl_state.mask.allow = 0x07; + + acls = posix_acl_alloc(6, GFP_KERNEL); + if (!acls) { + free_acl_state(&acl_state); + return -ENOMEM; + } + posix_state_to_acl(&acl_state, acls->a_entries); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 2, 0) + rc = set_posix_acl(user_ns, dentry, ACL_TYPE_ACCESS, acls); +#else + rc = set_posix_acl(user_ns, inode, ACL_TYPE_ACCESS, acls); +#endif +#else + rc = set_posix_acl(inode, ACL_TYPE_ACCESS, acls); +#endif + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", + rc); + else if (S_ISDIR(inode->i_mode)) { + posix_state_to_acl(&acl_state, acls->a_entries); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 2, 0) + rc = set_posix_acl(user_ns, dentry, ACL_TYPE_DEFAULT, acls); +#else + rc = set_posix_acl(user_ns, inode, ACL_TYPE_DEFAULT, acls); +#endif +#else + rc = set_posix_acl(inode, ACL_TYPE_DEFAULT, acls); +#endif + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", + rc); + } + free_acl_state(&acl_state); + posix_acl_release(acls); + return rc; +} + +int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns, + struct dentry *dentry, struct inode *parent_inode) +{ + struct posix_acl *acls; + struct posix_acl_entry *pace; + struct inode *inode = d_inode(dentry); + int rc, i; + + if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) + return -EOPNOTSUPP; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 2, 0) + acls = get_inode_acl(parent_inode, ACL_TYPE_DEFAULT); +#else + acls = get_acl(parent_inode, ACL_TYPE_DEFAULT); +#endif + if (!acls) + return -ENOENT; + pace = acls->a_entries; + + for (i = 0; i < acls->a_count; i++, pace++) { + if (pace->e_tag == ACL_MASK) { + pace->e_perm = 0x07; + break; + } + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 2, 0) + rc = set_posix_acl(user_ns, dentry, ACL_TYPE_ACCESS, acls); +#else + rc = set_posix_acl(user_ns, inode, ACL_TYPE_ACCESS, acls); +#endif +#else + rc = set_posix_acl(inode, ACL_TYPE_ACCESS, acls); +#endif + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", + rc); + if (S_ISDIR(inode->i_mode)) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 2, 0) + rc = set_posix_acl(user_ns, dentry, ACL_TYPE_DEFAULT, + acls); +#else + rc = set_posix_acl(user_ns, inode, ACL_TYPE_DEFAULT, + acls); +#endif +#else + rc = set_posix_acl(inode, ACL_TYPE_DEFAULT, acls); +#endif + if (rc < 0) + ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", + rc); + } + posix_acl_release(acls); + return rc; +} diff --git a/fs/ksmbd/vfs.h b/fs/ksmbd/vfs.h new file mode 100644 index 0000000000000..24f483ad4c6a1 --- /dev/null +++ b/fs/ksmbd/vfs.h @@ -0,0 +1,243 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + +#ifndef __KSMBD_VFS_H__ +#define __KSMBD_VFS_H__ + +#include +#include +#include +#include +#include +#include + +#include "smbacl.h" +#include "xattr.h" + +/* + * Enumeration for stream type. + */ +enum { + DATA_STREAM = 1, /* type $DATA */ + DIR_STREAM /* type $INDEX_ALLOCATION */ +}; + +/* CreateOptions */ +/* Flag is set, it must not be a file , valid for directory only */ +#define FILE_DIRECTORY_FILE_LE cpu_to_le32(0x00000001) +#define FILE_WRITE_THROUGH_LE cpu_to_le32(0x00000002) +#define FILE_SEQUENTIAL_ONLY_LE cpu_to_le32(0x00000004) + +/* Should not buffer on server*/ +#define FILE_NO_INTERMEDIATE_BUFFERING_LE cpu_to_le32(0x00000008) +/* MBZ */ +#define FILE_SYNCHRONOUS_IO_ALERT_LE cpu_to_le32(0x00000010) +/* MBZ */ +#define FILE_SYNCHRONOUS_IO_NONALERT_LE cpu_to_le32(0x00000020) + +/* Flaf must not be set for directory */ +#define FILE_NON_DIRECTORY_FILE_LE cpu_to_le32(0x00000040) + +/* Should be zero */ +#define CREATE_TREE_CONNECTION cpu_to_le32(0x00000080) +#define FILE_COMPLETE_IF_OPLOCKED_LE cpu_to_le32(0x00000100) +#define FILE_NO_EA_KNOWLEDGE_LE cpu_to_le32(0x00000200) +#define FILE_OPEN_REMOTE_INSTANCE cpu_to_le32(0x00000400) + +/** + * Doc says this is obsolete "open for recovery" flag should be zero + * in any case. + */ +#define CREATE_OPEN_FOR_RECOVERY cpu_to_le32(0x00000400) +#define FILE_RANDOM_ACCESS_LE cpu_to_le32(0x00000800) +#define FILE_DELETE_ON_CLOSE_LE cpu_to_le32(0x00001000) +#define FILE_OPEN_BY_FILE_ID_LE cpu_to_le32(0x00002000) +#define FILE_OPEN_FOR_BACKUP_INTENT_LE cpu_to_le32(0x00004000) +#define FILE_NO_COMPRESSION_LE cpu_to_le32(0x00008000) + +/* Should be zero*/ +#define FILE_OPEN_REQUIRING_OPLOCK cpu_to_le32(0x00010000) +#define FILE_DISALLOW_EXCLUSIVE cpu_to_le32(0x00020000) +#define FILE_RESERVE_OPFILTER_LE cpu_to_le32(0x00100000) +#define FILE_OPEN_REPARSE_POINT_LE cpu_to_le32(0x00200000) +#define FILE_OPEN_NO_RECALL_LE cpu_to_le32(0x00400000) + +/* Should be zero */ +#define FILE_OPEN_FOR_FREE_SPACE_QUERY_LE cpu_to_le32(0x00800000) +#define CREATE_OPTIONS_MASK cpu_to_le32(0x00FFFFFF) +#define CREATE_OPTION_READONLY 0x10000000 +/* system. NB not sent over wire */ +#define CREATE_OPTION_SPECIAL 0x20000000 + +struct ksmbd_work; +struct ksmbd_file; +struct ksmbd_conn; + +struct ksmbd_dir_info { + const char *name; +#ifdef CONFIG_SMB_INSECURE_SERVER + char *smb1_name; +#endif + char *wptr; + char *rptr; + int name_len; + int out_buf_len; + int num_entry; + int data_count; + int last_entry_offset; + bool hide_dot_file; + int flags; + int last_entry_off_align; +}; + +struct ksmbd_readdir_data { + struct dir_context ctx; + union { + void *private; + char *dirent; + }; + + unsigned int used; + unsigned int dirent_count; + unsigned int file_attr; + struct unicode_map *um; +}; + +/* ksmbd kstat wrapper to get valid create time when reading dir entry */ +struct ksmbd_kstat { + struct kstat *kstat; + unsigned long long create_time; + __le32 file_attributes; +}; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0) +static inline struct user_namespace *mnt_user_ns(const struct vfsmount *mnt) +{ + return &init_user_ns; +} + +static inline struct user_namespace *file_mnt_user_ns(struct file *file) +{ + return &init_user_ns; +} +#endif + +int ksmbd_vfs_lock_parent(struct user_namespace *user_ns, struct dentry *parent, + struct dentry *child); +int ksmbd_vfs_may_delete(struct user_namespace *user_ns, struct dentry *dentry); +int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns, + struct dentry *dentry, __le32 *daccess); +int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode); +int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode); +int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, + size_t count, loff_t *pos); +int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, + char *buf, size_t count, loff_t *pos, bool sync, + ssize_t *written); +int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id); +int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name); +int ksmbd_vfs_link(struct ksmbd_work *work, + const char *oldname, const char *newname); +int ksmbd_vfs_getattr(const struct path *path, struct kstat *stat); +#ifdef CONFIG_SMB_INSECURE_SERVER +int ksmbd_vfs_setattr(struct ksmbd_work *work, const char *name, + u64 fid, struct iattr *attrs); +int ksmbd_vfs_symlink(struct ksmbd_work *work, + const char *name, const char *symname); +int ksmbd_vfs_readlink(struct path *path, char *buf, int lenp); +int ksmbd_vfs_readdir_name(struct ksmbd_work *work, + struct user_namespace *user_ns, + struct ksmbd_kstat *ksmbd_kstat, + const char *de_name, int de_name_len, + const char *dir_path); +#endif +int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp, + char *newname); +int ksmbd_vfs_truncate(struct ksmbd_work *work, + struct ksmbd_file *fp, loff_t size); +struct srv_copychunk; +int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, + struct ksmbd_file *src_fp, + struct ksmbd_file *dst_fp, + struct srv_copychunk *chunks, + unsigned int chunk_count, + unsigned int *chunk_count_written, + unsigned int *chunk_size_written, + loff_t *total_size_written); +struct ksmbd_file *ksmbd_vfs_dentry_open(struct ksmbd_work *work, + const struct path *path, int flags, + __le32 option, int fexist); +ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list); +ssize_t ksmbd_vfs_getxattr(struct user_namespace *user_ns, + struct dentry *dentry, + char *xattr_name, + char **xattr_buf); +ssize_t ksmbd_vfs_casexattr_len(struct user_namespace *user_ns, + struct dentry *dentry, char *attr_name, + int attr_name_len); +int ksmbd_vfs_setxattr(struct user_namespace *user_ns, + struct dentry *dentry, const char *attr_name, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + void *attr_value, size_t attr_size, int flags); +#else + const void *attr_value, size_t attr_size, int flags); +#endif +int ksmbd_vfs_fsetxattr(struct ksmbd_work *work, const char *filename, + const char *attr_name, const void *attr_value, + size_t attr_size, int flags); +int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, + size_t *xattr_stream_name_size, int s_type); +int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns, + struct dentry *dentry, char *attr_name); +int ksmbd_vfs_kern_path(struct ksmbd_work *work, + char *name, unsigned int flags, struct path *path, + bool caseless); +struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, + const char *name, + unsigned int flags, + struct path *path); +int ksmbd_vfs_empty_dir(struct ksmbd_file *fp); +void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option); +int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, + loff_t off, loff_t len); +struct file_allocated_range_buffer; +int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, + struct file_allocated_range_buffer *ranges, + unsigned int in_count, unsigned int *out_count); +int ksmbd_vfs_unlink(struct user_namespace *user_ns, + struct dentry *dir, struct dentry *dentry); +void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat); +int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, + struct user_namespace *user_ns, + struct dentry *dentry, + struct ksmbd_kstat *ksmbd_kstat); +void ksmbd_vfs_posix_lock_wait(struct file_lock *flock); +int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout); +void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock); +int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns, + struct dentry *dentry); +int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, + struct dentry *dentry); +int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, + struct dentry *dentry, + struct smb_ntsd *pntsd, int len); +int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, + struct user_namespace *user_ns, + struct dentry *dentry, + struct smb_ntsd **pntsd); +int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns, + struct dentry *dentry, + struct xattr_dos_attrib *da); +int ksmbd_vfs_get_dos_attrib_xattr(struct user_namespace *user_ns, + struct dentry *dentry, + struct xattr_dos_attrib *da); +int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns, + struct dentry *dentry); +int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns, + struct dentry *dentry, + struct inode *parent_inode); +#endif /* __KSMBD_VFS_H__ */ diff --git a/fs/ksmbd/vfs_cache.c b/fs/ksmbd/vfs_cache.c new file mode 100644 index 0000000000000..74433e386122a --- /dev/null +++ b/fs/ksmbd/vfs_cache.c @@ -0,0 +1,763 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2016 Namjae Jeon + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include + +#include "glob.h" +#include "vfs_cache.h" +#include "oplock.h" +#include "vfs.h" +#include "connection.h" +#include "mgmt/tree_connect.h" +#include "mgmt/user_session.h" +#include "smb_common.h" + +#define S_DEL_PENDING 1 +#define S_DEL_ON_CLS 2 +#define S_DEL_ON_CLS_STREAM 8 + +static unsigned int inode_hash_mask __read_mostly; +static unsigned int inode_hash_shift __read_mostly; +static struct hlist_head *inode_hashtable __read_mostly; +static DEFINE_RWLOCK(inode_hash_lock); + +static struct ksmbd_file_table global_ft; +static atomic_long_t fd_limit; +static struct kmem_cache *filp_cache; + +void ksmbd_set_fd_limit(unsigned long limit) +{ + limit = min(limit, get_max_files()); + atomic_long_set(&fd_limit, limit); +} + +static bool fd_limit_depleted(void) +{ + long v = atomic_long_dec_return(&fd_limit); + + if (v >= 0) + return false; + atomic_long_inc(&fd_limit); + return true; +} + +static void fd_limit_close(void) +{ + atomic_long_inc(&fd_limit); +} + +/* + * INODE hash + */ + +static unsigned long inode_hash(struct super_block *sb, unsigned long hashval) +{ + unsigned long tmp; + + tmp = (hashval * (unsigned long)sb) ^ (GOLDEN_RATIO_PRIME + hashval) / + L1_CACHE_BYTES; + tmp = tmp ^ ((tmp ^ GOLDEN_RATIO_PRIME) >> inode_hash_shift); + return tmp & inode_hash_mask; +} + +static struct ksmbd_inode *__ksmbd_inode_lookup(struct inode *inode) +{ + struct hlist_head *head = inode_hashtable + + inode_hash(inode->i_sb, inode->i_ino); + struct ksmbd_inode *ci = NULL, *ret_ci = NULL; + + hlist_for_each_entry(ci, head, m_hash) { + if (ci->m_inode == inode) { + if (atomic_inc_not_zero(&ci->m_count)) + ret_ci = ci; + break; + } + } + return ret_ci; +} + +static struct ksmbd_inode *ksmbd_inode_lookup(struct ksmbd_file *fp) +{ + return __ksmbd_inode_lookup(file_inode(fp->filp)); +} + +static struct ksmbd_inode *ksmbd_inode_lookup_by_vfsinode(struct inode *inode) +{ + struct ksmbd_inode *ci; + + read_lock(&inode_hash_lock); + ci = __ksmbd_inode_lookup(inode); + read_unlock(&inode_hash_lock); + return ci; +} + +int ksmbd_query_inode_status(struct inode *inode) +{ + struct ksmbd_inode *ci; + int ret = KSMBD_INODE_STATUS_UNKNOWN; + + read_lock(&inode_hash_lock); + ci = __ksmbd_inode_lookup(inode); + if (ci) { + ret = KSMBD_INODE_STATUS_OK; + if (ci->m_flags & S_DEL_PENDING) + ret = KSMBD_INODE_STATUS_PENDING_DELETE; + atomic_dec(&ci->m_count); + } + read_unlock(&inode_hash_lock); + return ret; +} + +bool ksmbd_inode_pending_delete(struct ksmbd_file *fp) +{ + return (fp->f_ci->m_flags & S_DEL_PENDING); +} + +void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp) +{ + fp->f_ci->m_flags |= S_DEL_PENDING; +} + +void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp) +{ + fp->f_ci->m_flags &= ~S_DEL_PENDING; +} + +void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp, + int file_info) +{ + if (ksmbd_stream_fd(fp)) { + fp->f_ci->m_flags |= S_DEL_ON_CLS_STREAM; + return; + } + + fp->f_ci->m_flags |= S_DEL_ON_CLS; +} + +static void ksmbd_inode_hash(struct ksmbd_inode *ci) +{ + struct hlist_head *b = inode_hashtable + + inode_hash(ci->m_inode->i_sb, ci->m_inode->i_ino); + + hlist_add_head(&ci->m_hash, b); +} + +static void ksmbd_inode_unhash(struct ksmbd_inode *ci) +{ + write_lock(&inode_hash_lock); + hlist_del_init(&ci->m_hash); + write_unlock(&inode_hash_lock); +} + +static int ksmbd_inode_init(struct ksmbd_inode *ci, struct ksmbd_file *fp) +{ + ci->m_inode = file_inode(fp->filp); + atomic_set(&ci->m_count, 1); + atomic_set(&ci->op_count, 0); + atomic_set(&ci->sop_count, 0); + ci->m_flags = 0; + ci->m_fattr = 0; + INIT_LIST_HEAD(&ci->m_fp_list); + INIT_LIST_HEAD(&ci->m_op_list); + rwlock_init(&ci->m_lock); + return 0; +} + +static struct ksmbd_inode *ksmbd_inode_get(struct ksmbd_file *fp) +{ + struct ksmbd_inode *ci, *tmpci; + int rc; + + read_lock(&inode_hash_lock); + ci = ksmbd_inode_lookup(fp); + read_unlock(&inode_hash_lock); + if (ci) + return ci; + + ci = kmalloc(sizeof(struct ksmbd_inode), GFP_KERNEL); + if (!ci) + return NULL; + + rc = ksmbd_inode_init(ci, fp); + if (rc) { + pr_err("inode initialized failed\n"); + kfree(ci); + return NULL; + } + + write_lock(&inode_hash_lock); + tmpci = ksmbd_inode_lookup(fp); + if (!tmpci) { + ksmbd_inode_hash(ci); + } else { + kfree(ci); + ci = tmpci; + } + write_unlock(&inode_hash_lock); + return ci; +} + +static void ksmbd_inode_free(struct ksmbd_inode *ci) +{ + ksmbd_inode_unhash(ci); + kfree(ci); +} + +static void ksmbd_inode_put(struct ksmbd_inode *ci) +{ + if (atomic_dec_and_test(&ci->m_count)) + ksmbd_inode_free(ci); +} + +int __init ksmbd_inode_hash_init(void) +{ + unsigned int loop; + unsigned long numentries = 16384; + unsigned long bucketsize = sizeof(struct hlist_head); + unsigned long size; + + inode_hash_shift = ilog2(numentries); + inode_hash_mask = (1 << inode_hash_shift) - 1; + + size = bucketsize << inode_hash_shift; + + /* init master fp hash table */ + inode_hashtable = vmalloc(size); + if (!inode_hashtable) + return -ENOMEM; + + for (loop = 0; loop < (1U << inode_hash_shift); loop++) + INIT_HLIST_HEAD(&inode_hashtable[loop]); + return 0; +} + +void ksmbd_release_inode_hash(void) +{ + vfree(inode_hashtable); +} + +static void __ksmbd_inode_close(struct ksmbd_file *fp) +{ + struct dentry *dir, *dentry; + struct ksmbd_inode *ci = fp->f_ci; + int err; + struct file *filp; + + filp = fp->filp; + if (ksmbd_stream_fd(fp) && (ci->m_flags & S_DEL_ON_CLS_STREAM)) { + ci->m_flags &= ~S_DEL_ON_CLS_STREAM; + err = ksmbd_vfs_remove_xattr(file_mnt_user_ns(filp), + filp->f_path.dentry, + fp->stream.name); + if (err) + pr_err("remove xattr failed : %s\n", + fp->stream.name); + } + + if (atomic_dec_and_test(&ci->m_count)) { + write_lock(&ci->m_lock); + if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_PENDING)) { + dentry = filp->f_path.dentry; + dir = dentry->d_parent; + ci->m_flags &= ~(S_DEL_ON_CLS | S_DEL_PENDING); + write_unlock(&ci->m_lock); + ksmbd_vfs_unlink(file_mnt_user_ns(filp), dir, dentry); + write_lock(&ci->m_lock); + } + write_unlock(&ci->m_lock); + + ksmbd_inode_free(ci); + } +} + +static void __ksmbd_remove_durable_fd(struct ksmbd_file *fp) +{ + if (!has_file_id(fp->persistent_id)) + return; + + write_lock(&global_ft.lock); + idr_remove(global_ft.idr, fp->persistent_id); + write_unlock(&global_ft.lock); +} + +static void __ksmbd_remove_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) +{ + if (!has_file_id(fp->volatile_id)) + return; + + write_lock(&fp->f_ci->m_lock); + list_del_init(&fp->node); + write_unlock(&fp->f_ci->m_lock); + + write_lock(&ft->lock); + idr_remove(ft->idr, fp->volatile_id); + write_unlock(&ft->lock); +} + +static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) +{ + struct file *filp; + struct ksmbd_lock *smb_lock, *tmp_lock; + + fd_limit_close(); + __ksmbd_remove_durable_fd(fp); + __ksmbd_remove_fd(ft, fp); + + close_id_del_oplock(fp); + filp = fp->filp; + + __ksmbd_inode_close(fp); + if (!IS_ERR_OR_NULL(filp)) + fput(filp); + + /* because the reference count of fp is 0, it is guaranteed that + * there are not accesses to fp->lock_list. + */ + list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) { + spin_lock(&fp->conn->llist_lock); + list_del(&smb_lock->clist); + spin_unlock(&fp->conn->llist_lock); + + list_del(&smb_lock->flist); + locks_free_lock(smb_lock->fl); + kfree(smb_lock); + } +#ifdef CONFIG_SMB_INSECURE_SERVER + kfree(fp->filename); +#endif + if (ksmbd_stream_fd(fp)) + kfree(fp->stream.name); + kmem_cache_free(filp_cache, fp); +} + +static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp) +{ + if (!atomic_inc_not_zero(&fp->refcount)) + return NULL; + return fp; +} + +static struct ksmbd_file *__ksmbd_lookup_fd(struct ksmbd_file_table *ft, + u64 id) +{ + struct ksmbd_file *fp; + + if (!has_file_id(id)) + return NULL; + + read_lock(&ft->lock); + fp = idr_find(ft->idr, id); + if (fp) + fp = ksmbd_fp_get(fp); + read_unlock(&ft->lock); + return fp; +} + +static void __put_fd_final(struct ksmbd_work *work, struct ksmbd_file *fp) +{ + __ksmbd_close_fd(&work->sess->file_table, fp); + atomic_dec(&work->conn->stats.open_files_count); +} + +static void set_close_state_blocked_works(struct ksmbd_file *fp) +{ + struct ksmbd_work *cancel_work, *ctmp; + + spin_lock(&fp->f_lock); + list_for_each_entry_safe(cancel_work, ctmp, &fp->blocked_works, + fp_entry) { + list_del(&cancel_work->fp_entry); + cancel_work->state = KSMBD_WORK_CLOSED; + cancel_work->cancel_fn(cancel_work->cancel_argv); + } + spin_unlock(&fp->f_lock); +} + +int ksmbd_close_fd(struct ksmbd_work *work, u64 id) +{ + struct ksmbd_file *fp; + struct ksmbd_file_table *ft; + + if (!has_file_id(id)) + return 0; + + ft = &work->sess->file_table; + read_lock(&ft->lock); + fp = idr_find(ft->idr, id); + if (fp) { + set_close_state_blocked_works(fp); + + if (!atomic_dec_and_test(&fp->refcount)) + fp = NULL; + } + read_unlock(&ft->lock); + + if (!fp) + return -EINVAL; + + __put_fd_final(work, fp); + return 0; +} + +void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp) +{ + if (!fp) + return; + + if (!atomic_dec_and_test(&fp->refcount)) + return; + __put_fd_final(work, fp); +} + +static bool __sanity_check(struct ksmbd_tree_connect *tcon, struct ksmbd_file *fp) +{ + if (!fp) + return false; + if (fp->tcon != tcon) + return false; + return true; +} + +struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id) +{ + return __ksmbd_lookup_fd(&work->sess->file_table, id); +} + +struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, u64 id) +{ + struct ksmbd_file *fp = __ksmbd_lookup_fd(&work->sess->file_table, id); + + if (__sanity_check(work->tcon, fp)) + return fp; + + ksmbd_fd_put(work, fp); + return NULL; +} + +struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id, + u64 pid) +{ + struct ksmbd_file *fp; + + if (!has_file_id(id)) { + id = work->compound_fid; + pid = work->compound_pfid; + } + + fp = __ksmbd_lookup_fd(&work->sess->file_table, id); + if (!__sanity_check(work->tcon, fp)) { + ksmbd_fd_put(work, fp); + return NULL; + } + if (fp->persistent_id != pid) { + ksmbd_fd_put(work, fp); + return NULL; + } + return fp; +} + +struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id) +{ + return __ksmbd_lookup_fd(&global_ft, id); +} + +struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid) +{ + struct ksmbd_file *fp = NULL; + unsigned int id; + + read_lock(&global_ft.lock); + idr_for_each_entry(global_ft.idr, fp, id) { + if (!memcmp(fp->create_guid, + cguid, + SMB2_CREATE_GUID_SIZE)) { + fp = ksmbd_fp_get(fp); + break; + } + } + read_unlock(&global_ft.lock); + + return fp; +} + +#ifdef CONFIG_SMB_INSECURE_SERVER +struct ksmbd_file *ksmbd_lookup_fd_filename(struct ksmbd_work *work, char *filename) +{ + struct ksmbd_file *fp = NULL; + unsigned int id; + char *pathname; + + pathname = kmalloc(PATH_MAX, GFP_KERNEL); + if (!pathname) + return NULL; + + read_lock(&work->sess->file_table.lock); + idr_for_each_entry(work->sess->file_table.idr, fp, id) { + char *path = d_path(&fp->filp->f_path, pathname, PATH_MAX); + + if (IS_ERR(path)) + break; + + if (!strcmp(path, filename)) { + fp = ksmbd_fp_get(fp); + break; + } + } + read_unlock(&work->sess->file_table.lock); + + kfree(pathname); + return fp; +} +#endif + +struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode) +{ + struct ksmbd_file *lfp; + struct ksmbd_inode *ci; + + ci = ksmbd_inode_lookup_by_vfsinode(inode); + if (!ci) + return NULL; + + read_lock(&ci->m_lock); + list_for_each_entry(lfp, &ci->m_fp_list, node) { + if (inode == file_inode(lfp->filp)) { + atomic_dec(&ci->m_count); + lfp = ksmbd_fp_get(lfp); + read_unlock(&ci->m_lock); + return lfp; + } + } + atomic_dec(&ci->m_count); + read_unlock(&ci->m_lock); + return NULL; +} + +#define OPEN_ID_TYPE_VOLATILE_ID (0) +#define OPEN_ID_TYPE_PERSISTENT_ID (1) + +static void __open_id_set(struct ksmbd_file *fp, u64 id, int type) +{ + if (type == OPEN_ID_TYPE_VOLATILE_ID) + fp->volatile_id = id; + if (type == OPEN_ID_TYPE_PERSISTENT_ID) + fp->persistent_id = id; +} + +static int __open_id(struct ksmbd_file_table *ft, struct ksmbd_file *fp, + int type) +{ + u64 id = 0; + int ret; + + if (type == OPEN_ID_TYPE_VOLATILE_ID && fd_limit_depleted()) { + __open_id_set(fp, KSMBD_NO_FID, type); + return -EMFILE; + } + + idr_preload(GFP_KERNEL); + write_lock(&ft->lock); +#ifdef CONFIG_SMB_INSECURE_SERVER + ret = idr_alloc_cyclic(ft->idr, fp, 0, + IS_SMB2(fp->conn) ? INT_MAX - 1 : 0xFFFF, + GFP_NOWAIT); +#else + ret = idr_alloc_cyclic(ft->idr, fp, 0, INT_MAX - 1, GFP_NOWAIT); +#endif + if (ret >= 0) { + id = ret; + ret = 0; + } else { + id = KSMBD_NO_FID; + fd_limit_close(); + } + + __open_id_set(fp, id, type); + write_unlock(&ft->lock); + idr_preload_end(); + return ret; +} + +unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp) +{ + __open_id(&global_ft, fp, OPEN_ID_TYPE_PERSISTENT_ID); + return fp->persistent_id; +} + +struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) +{ + struct ksmbd_file *fp; + int ret; + + fp = kmem_cache_zalloc(filp_cache, GFP_KERNEL); + if (!fp) { + pr_err("Failed to allocate memory\n"); + return ERR_PTR(-ENOMEM); + } + + INIT_LIST_HEAD(&fp->blocked_works); + INIT_LIST_HEAD(&fp->node); + INIT_LIST_HEAD(&fp->lock_list); + spin_lock_init(&fp->f_lock); + atomic_set(&fp->refcount, 1); + + fp->filp = filp; + fp->conn = work->conn; + fp->tcon = work->tcon; + fp->volatile_id = KSMBD_NO_FID; + fp->persistent_id = KSMBD_NO_FID; + fp->f_ci = ksmbd_inode_get(fp); + + if (!fp->f_ci) { + ret = -ENOMEM; + goto err_out; + } + + ret = __open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID); + if (ret) { + ksmbd_inode_put(fp->f_ci); + goto err_out; + } + + atomic_inc(&work->conn->stats.open_files_count); + return fp; + +err_out: + kmem_cache_free(filp_cache, fp); + return ERR_PTR(ret); +} + +static int +__close_file_table_ids(struct ksmbd_file_table *ft, + struct ksmbd_tree_connect *tcon, + bool (*skip)(struct ksmbd_tree_connect *tcon, + struct ksmbd_file *fp)) +{ + unsigned int id; + struct ksmbd_file *fp; + int num = 0; + + idr_for_each_entry(ft->idr, fp, id) { + if (skip(tcon, fp)) + continue; + + set_close_state_blocked_works(fp); + + if (!atomic_dec_and_test(&fp->refcount)) + continue; + __ksmbd_close_fd(ft, fp); + num++; + } + return num; +} + +static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon, + struct ksmbd_file *fp) +{ + return fp->tcon != tcon; +} + +static bool session_fd_check(struct ksmbd_tree_connect *tcon, + struct ksmbd_file *fp) +{ + return false; +} + +void ksmbd_close_tree_conn_fds(struct ksmbd_work *work) +{ + int num = __close_file_table_ids(&work->sess->file_table, + work->tcon, + tree_conn_fd_check); + + atomic_sub(num, &work->conn->stats.open_files_count); +} + +void ksmbd_close_session_fds(struct ksmbd_work *work) +{ + int num = __close_file_table_ids(&work->sess->file_table, + work->tcon, + session_fd_check); + + atomic_sub(num, &work->conn->stats.open_files_count); +} + +int ksmbd_init_global_file_table(void) +{ + return ksmbd_init_file_table(&global_ft); +} + +void ksmbd_free_global_file_table(void) +{ + struct ksmbd_file *fp = NULL; + unsigned int id; + + idr_for_each_entry(global_ft.idr, fp, id) { + __ksmbd_remove_durable_fd(fp); + kmem_cache_free(filp_cache, fp); + } + + ksmbd_destroy_file_table(&global_ft); +} + +int ksmbd_file_table_flush(struct ksmbd_work *work) +{ + struct ksmbd_file *fp = NULL; + unsigned int id; + int ret; + + read_lock(&work->sess->file_table.lock); + idr_for_each_entry(work->sess->file_table.idr, fp, id) { + ret = ksmbd_vfs_fsync(work, fp->volatile_id, KSMBD_NO_FID); + if (ret) + break; + } + read_unlock(&work->sess->file_table.lock); + return ret; +} + +int ksmbd_init_file_table(struct ksmbd_file_table *ft) +{ + ft->idr = kzalloc(sizeof(struct idr), GFP_KERNEL); + if (!ft->idr) + return -ENOMEM; + + idr_init(ft->idr); + rwlock_init(&ft->lock); + return 0; +} + +void ksmbd_destroy_file_table(struct ksmbd_file_table *ft) +{ + if (!ft->idr) + return; + + __close_file_table_ids(ft, NULL, session_fd_check); + idr_destroy(ft->idr); + kfree(ft->idr); + ft->idr = NULL; +} + +int ksmbd_init_file_cache(void) +{ + filp_cache = kmem_cache_create("ksmbd_file_cache", + sizeof(struct ksmbd_file), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (!filp_cache) + goto out; + + return 0; + +out: + pr_err("failed to allocate file cache\n"); + return -ENOMEM; +} + +void ksmbd_exit_file_cache(void) +{ + kmem_cache_destroy(filp_cache); +} diff --git a/fs/ksmbd/vfs_cache.h b/fs/ksmbd/vfs_cache.h new file mode 100644 index 0000000000000..ef99bf22a1581 --- /dev/null +++ b/fs/ksmbd/vfs_cache.h @@ -0,0 +1,184 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2019 Samsung Electronics Co., Ltd. + */ + +#ifndef __VFS_CACHE_H__ +#define __VFS_CACHE_H__ + +#include +#include +#include +#include +#include +#include + +#include "vfs.h" + +/* Windows style file permissions for extended response */ +#define FILE_GENERIC_ALL 0x1F01FF +#define FILE_GENERIC_READ 0x120089 +#define FILE_GENERIC_WRITE 0x120116 +#define FILE_GENERIC_EXECUTE 0X1200a0 + +#define KSMBD_START_FID 0 +#define KSMBD_NO_FID (INT_MAX) +#define SMB2_NO_FID (0xFFFFFFFFFFFFFFFFULL) + +struct ksmbd_conn; +struct ksmbd_session; + +struct ksmbd_lock { + struct file_lock *fl; + struct list_head clist; + struct list_head flist; + struct list_head llist; + unsigned int flags; + int cmd; + int zero_len; + unsigned long long start; + unsigned long long end; +}; + +struct stream { + char *name; + ssize_t size; +}; + +struct ksmbd_inode { + rwlock_t m_lock; + atomic_t m_count; + atomic_t op_count; + /* opinfo count for streams */ + atomic_t sop_count; + struct inode *m_inode; + unsigned int m_flags; + struct hlist_node m_hash; + struct list_head m_fp_list; + struct list_head m_op_list; + struct oplock_info *m_opinfo; + __le32 m_fattr; +}; + +struct ksmbd_file { + struct file *filp; + u64 persistent_id; + u64 volatile_id; + + spinlock_t f_lock; + + struct ksmbd_inode *f_ci; + struct ksmbd_inode *f_parent_ci; + struct oplock_info __rcu *f_opinfo; + struct ksmbd_conn *conn; + struct ksmbd_tree_connect *tcon; + + atomic_t refcount; + __le32 daccess; + __le32 saccess; + __le32 coption; + __le32 cdoption; + __u64 create_time; + __u64 itime; + + bool is_nt_open; + bool attrib_only; + + char client_guid[16]; + char create_guid[16]; + char app_instance_id[16]; + + struct stream stream; + struct list_head node; + struct list_head blocked_works; + struct list_head lock_list; + + int durable_timeout; + +#ifdef CONFIG_SMB_INSECURE_SERVER + /* for SMB1 */ + int pid; + + /* conflict lock fail count for SMB1 */ + unsigned int cflock_cnt; + /* last lock failure start offset for SMB1 */ + unsigned long long llock_fstart; + + int dirent_offset; + + /* for find_first/find_next */ + char *filename; +#endif + /* if ls is happening on directory, below is valid*/ + struct ksmbd_readdir_data readdir_data; + int dot_dotdot[2]; +}; + +static inline void set_ctx_actor(struct dir_context *ctx, + filldir_t actor) +{ + ctx->actor = actor; +} + +#define KSMBD_NR_OPEN_DEFAULT BITS_PER_LONG + +struct ksmbd_file_table { + rwlock_t lock; + struct idr *idr; +}; + +static inline bool has_file_id(u64 id) +{ + return id < KSMBD_NO_FID; +} + +static inline bool ksmbd_stream_fd(struct ksmbd_file *fp) +{ + return fp->stream.name != NULL; +} + +int ksmbd_init_file_table(struct ksmbd_file_table *ft); +void ksmbd_destroy_file_table(struct ksmbd_file_table *ft); +int ksmbd_close_fd(struct ksmbd_work *work, u64 id); +struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, u64 id); +struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id); +struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id, + u64 pid); +void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp); +struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id); +struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid); +#ifdef CONFIG_SMB_INSECURE_SERVER +struct ksmbd_file *ksmbd_lookup_fd_filename(struct ksmbd_work *work, char *filename); +#endif +struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode); +unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp); +struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp); +void ksmbd_close_tree_conn_fds(struct ksmbd_work *work); +void ksmbd_close_session_fds(struct ksmbd_work *work); +int ksmbd_close_inode_fds(struct ksmbd_work *work, struct inode *inode); +int ksmbd_init_global_file_table(void); +void ksmbd_free_global_file_table(void); +int ksmbd_file_table_flush(struct ksmbd_work *work); +void ksmbd_set_fd_limit(unsigned long limit); + +/* + * INODE hash + */ +int __init ksmbd_inode_hash_init(void); +void ksmbd_release_inode_hash(void); + +enum KSMBD_INODE_STATUS { + KSMBD_INODE_STATUS_OK, + KSMBD_INODE_STATUS_UNKNOWN, + KSMBD_INODE_STATUS_PENDING_DELETE, +}; + +int ksmbd_query_inode_status(struct inode *inode); +bool ksmbd_inode_pending_delete(struct ksmbd_file *fp); +void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp); +void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp); +void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp, + int file_info); +int ksmbd_init_file_cache(void); +void ksmbd_exit_file_cache(void); +#endif /* __VFS_CACHE_H__ */ diff --git a/fs/ksmbd/xattr.h b/fs/ksmbd/xattr.h new file mode 100644 index 0000000000000..8857c01093d92 --- /dev/null +++ b/fs/ksmbd/xattr.h @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2021 Samsung Electronics Co., Ltd. + */ + +#ifndef __XATTR_H__ +#define __XATTR_H__ + +/* + * These are on-disk structures to store additional metadata into xattr to + * reproduce windows filesystem semantics. And they are encoded with NDR to + * compatible with samba's xattr meta format. The compatibility with samba + * is important because it can lose the information(file attribute, + * creation time, acls) about the existing files when switching between + * ksmbd and samba. + */ + +/* + * Dos attribute flags used for what variable is valid. + */ +enum { + XATTR_DOSINFO_ATTRIB = 0x00000001, + XATTR_DOSINFO_EA_SIZE = 0x00000002, + XATTR_DOSINFO_SIZE = 0x00000004, + XATTR_DOSINFO_ALLOC_SIZE = 0x00000008, + XATTR_DOSINFO_CREATE_TIME = 0x00000010, + XATTR_DOSINFO_CHANGE_TIME = 0x00000020, + XATTR_DOSINFO_ITIME = 0x00000040 +}; + +/* + * Dos attribute structure which is compatible with samba's one. + * Storing it into the xattr named "DOSATTRIB" separately from inode + * allows ksmbd to faithfully reproduce windows filesystem semantics + * on top of a POSIX filesystem. + */ +struct xattr_dos_attrib { + __u16 version; /* version 3 or version 4 */ + __u32 flags; /* valid flags */ + __u32 attr; /* Dos attribute */ + __u32 ea_size; /* EA size */ + __u64 size; + __u64 alloc_size; + __u64 create_time; /* File creation time */ + __u64 change_time; /* File change time */ + __u64 itime; /* Invented/Initial time */ +}; + +/* + * Enumeration is used for computing posix acl hash. + */ +enum { + SMB_ACL_TAG_INVALID = 0, + SMB_ACL_USER, + SMB_ACL_USER_OBJ, + SMB_ACL_GROUP, + SMB_ACL_GROUP_OBJ, + SMB_ACL_OTHER, + SMB_ACL_MASK +}; + +#define SMB_ACL_READ 4 +#define SMB_ACL_WRITE 2 +#define SMB_ACL_EXECUTE 1 + +struct xattr_acl_entry { + int type; + uid_t uid; + gid_t gid; + mode_t perm; +}; + +/* + * xattr_smb_acl structure is used for computing posix acl hash. + */ +struct xattr_smb_acl { + int count; + int next; + struct xattr_acl_entry entries[0]; +}; + +/* 64bytes hash in xattr_ntacl is computed with sha256 */ +#define XATTR_SD_HASH_TYPE_SHA256 0x1 +#define XATTR_SD_HASH_SIZE 64 + +/* + * xattr_ntacl is used for storing ntacl and hashes. + * Hash is used for checking valid posix acl and ntacl in xattr. + */ +struct xattr_ntacl { + __u16 version; /* version 4*/ + void *sd_buf; + __u32 sd_size; + __u16 hash_type; /* hash type */ + __u8 desc[10]; /* posix_acl description */ + __u16 desc_len; + __u64 current_time; + __u8 hash[XATTR_SD_HASH_SIZE]; /* 64bytes hash for ntacl */ + __u8 posix_acl_hash[XATTR_SD_HASH_SIZE]; /* 64bytes hash for posix acl */ +}; + +/* DOS ATTRIBUITE XATTR PREFIX */ +#define DOS_ATTRIBUTE_PREFIX "DOSATTRIB" +#define DOS_ATTRIBUTE_PREFIX_LEN (sizeof(DOS_ATTRIBUTE_PREFIX) - 1) +#define XATTR_NAME_DOS_ATTRIBUTE (XATTR_USER_PREFIX DOS_ATTRIBUTE_PREFIX) +#define XATTR_NAME_DOS_ATTRIBUTE_LEN \ + (sizeof(XATTR_USER_PREFIX DOS_ATTRIBUTE_PREFIX) - 1) + +/* STREAM XATTR PREFIX */ +#define STREAM_PREFIX "DosStream." +#define STREAM_PREFIX_LEN (sizeof(STREAM_PREFIX) - 1) +#define XATTR_NAME_STREAM (XATTR_USER_PREFIX STREAM_PREFIX) +#define XATTR_NAME_STREAM_LEN (sizeof(XATTR_NAME_STREAM) - 1) + +/* SECURITY DESCRIPTOR(NTACL) XATTR PREFIX */ +#define SD_PREFIX "NTACL" +#define SD_PREFIX_LEN (sizeof(SD_PREFIX) - 1) +#define XATTR_NAME_SD (XATTR_SECURITY_PREFIX SD_PREFIX) +#define XATTR_NAME_SD_LEN \ + (sizeof(XATTR_SECURITY_PREFIX SD_PREFIX) - 1) + +#endif /* __XATTR_H__ */ From bb85cc970c7cf37e81d8185ab38a3a8b841923fe Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Thu, 7 Oct 2021 15:32:56 -0500 Subject: [PATCH 12/65] wire up ksmbd Signed-off-by: Robert Nelson --- fs/Kconfig | 1 + fs/Makefile | 1 + 2 files changed, 2 insertions(+) diff --git a/fs/Kconfig b/fs/Kconfig index 50ab89368c2b5..cacd0c33ad755 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -337,6 +337,7 @@ config NFS_COMMON source "net/sunrpc/Kconfig" source "fs/ceph/Kconfig" source "fs/cifs/Kconfig" +source "fs/ksmbd/Kconfig" source "fs/coda/Kconfig" source "fs/afs/Kconfig" source "fs/9p/Kconfig" diff --git a/fs/Makefile b/fs/Makefile index 6a1a2b40a653d..04a2b464d60df 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -96,6 +96,7 @@ obj-$(CONFIG_NLS) += nls/ obj-$(CONFIG_UNICODE) += unicode/ obj-$(CONFIG_SYSV_FS) += sysv/ obj-$(CONFIG_CIFS) += cifs/ +obj-$(CONFIG_SMB_SERVER) += ksmbd/ obj-$(CONFIG_HPFS_FS) += hpfs/ obj-$(CONFIG_NTFS_FS) += ntfs/ obj-$(CONFIG_UFS_FS) += ufs/ From 9ba19d280d92b4db02671e47c5b781bee1841ad2 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Fri, 3 Feb 2023 19:23:49 -0600 Subject: [PATCH 13/65] Add wireless-regdb regulatory database file https://git.kernel.org/pub/scm/linux/kernel/git/sforshee/wireless-regdb.git/commit/?id=fe05cc94e070c71486047002fc2edf68f5f2a07a Signed-off-by: Robert Nelson --- firmware/regulatory.db | Bin 0 -> 4592 bytes firmware/regulatory.db.p7s | Bin 0 -> 1182 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 firmware/regulatory.db create mode 100644 firmware/regulatory.db.p7s diff --git a/firmware/regulatory.db b/firmware/regulatory.db new file mode 100644 index 0000000000000000000000000000000000000000..86f2548e5b508282f3f8c9b9b53778852f498a1c GIT binary patch literal 4592 zcmZuzeT-aH6~FhM)7{x^yF8j_#fobwZR040O;*Pes_x92*_qjyH}iHrcSbAJP|{6` zX(J6bkywln-H5>^Ob|6-b+KxK6(bf5x=0|3U9%0)5K7QwS&bjZ+RI=%m5W*ZAgCof_%qKG#PflVYnMW&Ggkwn?&18vih3r*WP1c~3 ztiy}RCfVBf-c%CiQz~v5~5g zy+xlk-eD!-yfukitQ^c+d5l{{oUlsps8yzO1r4hX&s#Ituv*Zu+R#Z;jdX!NHY}&h z^l4);W5H5}?3pYc$yDi^_$TwwnWWm275Hj41>ec0VI`ZvvFs#T*&N2RMf!H=Q-Myl z2FuwxT+FucV6K2WawUxCDsXSE2CKO`bna)4<5OkK zPdV^>Td2U(g(i*{TG%MG;htg&<3$U17xOS*ETB=WVxmYKMXFJ3 zK*vtvxNX5PJ58SqeMqaFgH^i#^L7avb{Y5E4&iCCS1@5yu3aa41LkbnhdqOFyM>if z7C%jjR_|S=N#IxL)=c5zG)Ut0b>ruIyTuVu;LJ}(}Y!r zbWbPYsY(W|N*Ov8>bX*f$0|*XSCg2i(oCvZc&b5Cc<#V&GaySPGoDJ-B3u^{2$fXAX*uznbV_I*$x32=Sq#+RtYl|IP`Gw?8Drj#Cu>HNwQF@rCO& zR()8+{uA+w*jfau;kgKAJz+jEVni|XKeE)T&+%dG`~T<&QQGS2V?Sfo%X-4t&-eX) zzSqxHgm>I|!p$c>$#}sKJ4ePe|4<)h{;9v76vgf2r+NnA(*0wiym(S$bKtnf&fAxS z+;!C4MEUIZFfHW=L5^oLHhCODP854NYhfPKpgH&RZi?E>KCF3o{-Hd>p!tV!kTd%# z>o-hK|Mc;L8Y{1ckB9p(-w)`jeykclQ6{H)LD z$Adk7?Pon6KkvhcVm%YS1ECmwhr(FzxX0n{D};@qo5T5LA9nXgI{A+9j(HA9I{7?Z zb3D8h#dHTEeyS0{q&dJe7YzFen<0LGkA6JQf%h81TI1%Z&FsTE=IUsSVTN>aov;pF zH>69~jbhD-bSFbv^qtfAxO?XU9WL(aa`yVv4KQ^>Sc&OJVET z=P0J%l!$-bVHeNu()IZsYrluN-!KmNeLvTu-h=wgAI7Zzvom65^a1xj0LL?a@8tj{N(S&UpdCH5cnyd&am=f%?Cp?y(h$9ny9Hs#^zFsb?pTeL%kIVn552`=FjWA8| z>$GjRe2r~{-53vhG5xl^Z-|=!wz;+a-02WkQ~ertx-q1EQQGRaun!KwVL<9}QMT0M zQ1`SPlfO_|t&RE4$aj#oG3v$E{Ac7Yh1A>26dwa2&dRsQhp4-EspsH&Xu$ySpOxFl zl2tEo|Ab$Xb+uhR4?ywfVHCza-W9n=eqVh71_*Qg6?s^mB|q2X^Evf( z7!6`OJxuym3fjuT{$GLly}OXse_Q`T+-Lovq-6nj5$^&RZQKW zmV=zUGSyb~UA3&|$y&Ok?RzkwweZ>337T)O{3hk1u17jRd2zGy$Ld}fW}l9`P5w|F z@O+2lOX|yRKibFr-5&JU4fX5RI3j-%@X(odK}UFT{#DvgZMAWvueNINmtU@+eugQ4sL-h6vsPqac@Um`Jg~XBoH`S6-zFVFa F;{RGWHMIZ$ literal 0 HcmV?d00001 diff --git a/firmware/regulatory.db.p7s b/firmware/regulatory.db.p7s new file mode 100644 index 0000000000000000000000000000000000000000..48614fdcd950194ea9fe0932ffd4d752efc4d7fd GIT binary patch literal 1182 zcmXqLVwuIpsnzDu_MMlJooPW6OSeH2OFI)IqanWmFB@k$Y57IP8L6oT z3gWzm=7t6aW`>p~2F9jQ68uJnhUNyAhDJ~U?An?bm5_}Gx`?@nk)HwR0xqT|Mn;CM z4v*((v{*8Kvb>(A<7gqerfz9-p=#ZPnvA>o_BXFbTFkG>3OdBBrCXQwMQ*cDfkO0^ z=|;AN-PY0^;v(6JN5Z?OiOKv`xysG?GefZD_o+ahi>{Kl1YEjq{wrJgH$hi|TVv`b z-B%qmc3cco{17bvktzSB+w95l8WZ@}nElF7E({O*cINW^TCJPX#jdY+skgqbs$Y7# z;kbd>rQmmO*2q0K{AjjlPV%wT=z7)CjD@c**D=J*io2m8VN}kpDU>E>R{VTAi=+9* zjYZmD7HTTgOsnM9zHsB-i5&aMzmLAF2oe;PuKM;UW9IYUZelHuk1u@o;F8AX=M0{IyM6Wwa{mIf7OHQT7tSL%AoG*M>*R=9_*YmGe6b-ztep`IAGVBw- z!1vkYwQ2f*qmwdu%Ti1qxCzl8GMutX;+>(KaknSwU#Nh zc=g}&Q_k>OCH%D!(HApbsVILsU}Hb?y9H@0#VSP%n;5qNQ^_V^qBc;#nzqnVFjAsl zkZB+RPtu&shRAuqKn5YhYRE6nYh+|#0ZhcECWZzUQ3m=5Sr$Vj7KH`s-iNFeKJu-} z5xeEd9_7=ZYWmGk?X>+@PSy97CoOj%M;$P6votX>G@tdbElc=?mVJX?az(sfTBe|TuG zVBcQVma`IBH}_dI{;vDoUUVREa_Z-E)^_%F-~B&Tol!o|aPjcwOb=_`^HWbZTHK2K zdVKw@lQn9oYxZu8yOz>+ Date: Mon, 21 Jun 2021 10:38:10 -0500 Subject: [PATCH 14/65] Add AM335x CM3 Power Managment Firmware http://git.ti.com/gitweb/?p=processor-firmware/ti-amx3-cm3-pm-firmware.git;a=commit;h=fb484c5e54f2e31cf0a338d2927a06a2870bcc2c Signed-off-by: Robert Nelson --- firmware/am335x-bone-scale-data.bin | Bin 0 -> 73 bytes firmware/am335x-evm-scale-data.bin | Bin 0 -> 17 bytes firmware/am335x-pm-firmware.bin | Bin 0 -> 11324 bytes firmware/am335x-pm-firmware.elf | Bin 0 -> 217148 bytes firmware/am43x-evm-scale-data.bin | Bin 0 -> 41 bytes 5 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 firmware/am335x-bone-scale-data.bin create mode 100644 firmware/am335x-evm-scale-data.bin create mode 100755 firmware/am335x-pm-firmware.bin create mode 100755 firmware/am335x-pm-firmware.elf create mode 100644 firmware/am43x-evm-scale-data.bin diff --git a/firmware/am335x-bone-scale-data.bin b/firmware/am335x-bone-scale-data.bin new file mode 100644 index 0000000000000000000000000000000000000000..1ce3c1c596d7a7f400b0cc89bda5a41eed2780c5 GIT binary patch literal 73 pcmd-HXHZUIU{c}EWl|AfLZWk+R0P|Ad@#)bSHb~R0-{lr003gr3L5|b literal 0 HcmV?d00001 diff --git a/firmware/am335x-evm-scale-data.bin b/firmware/am335x-evm-scale-data.bin new file mode 100644 index 0000000000000000000000000000000000000000..a222389d233fa8f1c76ef35470b57284999c8df5 GIT binary patch literal 17 Ucmd-HXJAiZVA55UX8=>$024d{B>(^b literal 0 HcmV?d00001 diff --git a/firmware/am335x-pm-firmware.bin b/firmware/am335x-pm-firmware.bin new file mode 100755 index 0000000000000000000000000000000000000000..7d66669f2dcf03629b562ac7b9b8c590489f2caa GIT binary patch literal 11324 zcmbta3w#vS)xWbdn{2Y1WJ3}N$rdJAQUe>}3Rt7~SdwM(SRz5R0*YM(bOI;=e1z7P z4anChs6ez0L@gBcM=PZXCY2Q8;s>^+_OV-`Rk0=1k{~JVEQ#!HUi1CW>~4sn->;9& z?>}?zx#ygF?z#8e*Myl3Y^Ra2Pk@U+0QeLL1E2p5fyS94R6X7=AphUu#DAgZTvT>; z5@QR2mw*;vv`z0I{{e6j;F1~3044*s0P}$5zwxr~$tM=Z}*WXnwlNu~fYFX{};;L4U4O-PxN z@P2#+-?NF=0gW=VXL>mias)5&F=k-aib<|iIa^j*t(CkIAD>%stvmPjW4%Z988ta` za?6hO>c{0&9P9Pu-QWh*%Gc!8Oejm2CsgQVqseWVUzb{!CYyZOWtMW!c{{6eH!|MI zp~faTqf9xHSz2vT{A0@#WPY;2*T}|{AJ61^o)nhJmXG*LlMvn`*s5Rfkt2-Kt7ofYa{Yx{~^5RD(ZA|tG-M>C$2oM!s_PTi$j9T=q8^K zKgp3!()~q#CR)?Mk_*gXh6MAHABkpFR6esLLUVv7xb*OK%%ho4jP~Pa%NBg5%-qJ5 zKMk@;O!;U~l(-p8**7?&AnbQt-kO$LF-G21oaIx4FRDyg8{(8pVNMwiPZXI_H+Vgs z-x*Ycuc!>EaiO_=C)UQ5^GJ7um}u@0C8lf~?CfUVBk;q5N$^OU8vLbd_YQ$Z>Fuh+ zYv_oiw;-Jh*>4YaoHsZqRds}THmHQ0QvbSZ+8=E&3>px_ouDRp9-N854l>W2V zym{@*FP-Mc>FZklzGnB7F3Hb#t$|UX-rLGw+?8 zfn~yO5Bl}cUvT%r#x5?1XFn6A zRQOtXE>YyS{Uy%Y=c#6a59>aRcLYAEGX~;$s}j$@7h=2lfWbJv&$Bwt_wxWtIpQtv zW9xq%V2KHRp8J?}t@KRDVt-aj3VbDQzNC9TRbrhFNN)@%m)O9V>2J>Y_}+yrdjm^M zM*`zhwtD9(%mgo*_V$?g(+686Q;3_HCb22!{PC~%X6atkHK3+-Yb2o|QL8Q9$2S>l zO!;RW-(=R_e@kN zN@bnBwyH%9erFhcoP-%9Kjf#9+xi9%a81m1TVLpaZr9!(T>?=z1Oo>W0_xYeTOAnf z%ZArk!u~nVt}ZjYwzRvZwnTC8g;KB!DOg@8UFuSU^M<(@r@AurEU>ihbciuyKrpg= zZrjmIhw5r-3#HBo_tHY?G^G>|H2hJcG0^;7X?<*kLs<&mxr*ITDE+GIdm(Py?ttC& zqkx?yd!w-7j%3yatP5;e39Z zp*w0*95+J^4h+2^ZP2VR!-|HkM)d#mI;*1N$ubVKr@HyVw+m5?4oFAm4y%H;pOcIiu+(T}!(_-v%0|+aBqSuhx~JxA`4v zaQ_g$OK2Le*PYr{yhGyN*GU^CZfADfo*tbp_rhNvV}WCyxPUtI))ph4)Eh4i9S*3s z1^x4+GUWbuXogd8{yAi59;ekOqCOjJhea6KJ3KV9-}5Ajv9V3#S{vWnp6kj)S-r=q z82A!J;0wB6?MdcpptEs^yGC&S^ITk+8hmmH+Pli6LW#UT7jxoq%qe{Ujl)^~Ljilr zSRdE4sYSP|DT~>-2IHUkfO;cF&y8fQG}NwH%Qab?dW&Q z($|cVw%4-!+xlMp3CZm0iujuRZ0i!V@%!8X>igUJwjB6&7g^QnujzU@uIJ>g_)kvW z7uS6NlQx$S7!DG{B4X=W1#}7Yik#*PSbq=Ak`_ zL+Fhg1?R0Tu@;^hjI?lu)(dx@L;tS}bv;%~Blk6JM&z`TMkd zq}5NITL8+VKONCpqx>xgGe1lC-MPP`uTxrC+oopW_4y&eI9}o!)ZmAMd^7e)Jp$G& z<{iekNUlh6?J7?CgTG|FGpQ^`;W^(h)WNMY?d3XsnK4~Dj6>=nM&&)UHy8alYZ)3f$`dN$QSJFvjhj@_T0 zH8bpbIqR%qPMOFtWuk6@x1yNkAM#GLJ5oJ3I=CGT4)*C12M?L6`S^ZaFY)Rs9I3@2 z(Oy&PImmaHq;*3KN{Ms(7Q;}YlYrtpx4kg3A8GWHVL#%8Z93*Nu%Y1wqB!62JL2zHW{?l z8f^+_(=^%?(3Wd78)*3&%?8>%8m$1d2^y^cw0RnBDro5%Z7OJ`8m$ntM2%Jm+ASLG zTF{tAyB4(R8qE&ck#g4h{FI~Ah|;G2&fC7nt*Z4^d$uh41bqtP0nyDmG&&^fT7 z*xyjz}2ENA#1t8~XEsOrR6k2Q&hS6*aBtE4ZFn zywLNu{vr9!&~@e}mMl+|PiFe69&o?v#*Ac!4#Ft$w$tpa*7@&+i|G(w?iCiE5mvBe$`rgI^J+I`1Dg zLKCxn;O{tJq_rsnK4}{GVK>DBvz_suz`8_jq%~_;<+OW+Bgl(5-?wUQ&voTq)%Goi zspUNbWNrSbSo=5ki*hE~|6WU^{fo8s9~h3b|NUWV|8v8U_HP-+yr#ALH^Ydd+Iw1J ztu5~V7_GJSzlWzZ@aygrk!)wmp|?J7XHi>@E9WZP)@ioAq}i4?V%rU{Z5(Vn9JMV) zv&|i~?P<-nSy9_=fo=Ry(r2s+hPz--XZVkJ4)p(a#HxodXI*WV#bvq5uKAi>^9Ewx z`bSul(_zuOQHy>Nrcp93YSApsqR>#pTYZ|RZWvC0O*}kR)DamO`@?^OJ#X~;yFYJ( zp{+!IKRPbkwHcPi*x0v<+Axa6Rfsili25*L#Gco*`u{W(=`BvH|Ikpr#G%((hpg)n z|2%Kg`Sj33F;e6pjTf=TQ_R-1#Yw}#ieZ*N+m~NCd5%rH7ZPyqbsFPrrmwNoj`EL& zSIXHKGu%n0IKwjPf4%<-XVViUhe>CNN_QtufN2XeiE! zMpq7Q5)Kau62;g{LljTDhG<@>8)8ZWaQz@tW}$8y=8a*TE19wzo;GWKS=|w*McAKx zB)4ynp4$iisNMNcT$ypt^h{LuYEAblP4|50O@Llb(|HqgJ~pCrHhfKTYoc=Ch1M!f z?gq$Rghz&h;;_R@Rxo8D_A$i2Fv>qu<1Yf|AE38rh`1AwnmtlOdWQjbbGM9)7gGnx z-x!t8*5orGpAIw+ll&)0<%Ox&F5|w;?4Y~2B;1!_)s)7|vlP#o@owBEa|9 z=1zEQct{P-z+#$BP{N$llqtZ?ODxuk~A3 zFxxc0wVK(k_b$iElQnf|>fz19CQp0oTPgW}Zg7H_Pag4@ti-(E;9^IKOk+hYI6 zNLOm5R959H>Y&|kZfg+E?!ie-CSq6MaH`6!Rm?Eo#ek<%n5s#9WwK zvnxzH>Fz)l$o3@0hHT7X?$L28AyfI8W+YMo|`iU_4V11bCdn`<~ zuhZ-@Ru;%FVh32%kx*fd^gVhGcSB*DytlY_#NvC~WA;&>Qf!>YdYuP!glT;+Vwa1x zL)nT5P=jyx3mE;6^y~0k(@!z+Fs|sBvQpdeQe-^vbGo}Xv{Cw-TG`_oUrdJN9|i^M zWIt2NwYWNhF}*5GGk-}t&Q>AJRB7?iGmN1F`|IJ}ZrV{}T}Ql=e)4Bg+uY0Qy2j#k zpCFM|@1S0Lzc46#N;cg#AV^6qqEysDr7z-68aKa~b?N?B4R(5U+Den%MROzRjN~Vd z~pZY%c1p^hZ&lx>Cj6U8*qXnmZ_iBaRk9u7Oca|I8%EQ+dyH+o5 zO48iU(9QSG3OP*VN2-tN*4hSE00~ z$~*C81fEaLaAlNDkQd7tbABMlx&I>Pxh>UcmEUz2x`kz9-Z1#sFTA5hsijzcVHdzyAb2gQUmz**^= z6~2y%TO4Wb$hfY=`7-wH>-jaEh{Mq`1z#vb=iZpiHpq}2yiM@acdQbHX|p%kL2Vjc zX8l~52*0Pxtl%b}RbFVh!B-_8>p8|(g{D`ro?~30Tp>>@nc}8=MRj2;?S4-FtvpS> zrDRIUG_5T0N?Fyj)l;D?i!2fD3e6DpJ;zz9e5{(*ByPpV6`BSgm29d`)mAEQ zMe!A?Ilekkz7P6yFU0E--QcTvTmB#UCi%LO+zXuUCZd>2!81~BcF2@t;9g`o=2tMb z$tI)gXwOk@k!)_&^}MY+iF2HX7iCe2=VmE-F2_A)Wg|udvkm&``(vaXiBTEiZIVgO zw*uc;x%kPvn@;3oR%#MRMd}cUpWTDqB+iH{Gv%WPBYJ2&#m?lra4IKzD3&5;^2AZG zgflr#n4HKhp^dgL$;{HYx4Y%*(5ZZ;g><&8PHyXY{A(JacvxBiy?X+u<3j} zcAWxp2HXxra*-JHpzdY)jNJ^(0Nw}o1FrxE7nh$cV-8~TLpii+VMlKA(mhAy)JHxe z`Jy(Hlpt4Fj6OZSYF~2a- z+spU7xAqS6yooIEj7Od9=*2TLzOqEvQ?UwI}J+6`=bR$65jZk*fq&2?NSH?zu@ zRDV(EKY1X;Y96S)Yr>C1%)R5_YnC73CVrZ9@nDZ-e~2|(om4(1#NozS@bvq+>mk4P zk@0-zJ@F|gxtj!^u^#`?pztY|eXwUOtvI3!HfZ1eNOtXFHs!dVi5($R)mYcq@=VXz ziX=}*$WWCrCj)PxDi6mg{{=YqG@hvMR>O?)JMwRtM=9-S7t5VU>^d zawWViNGJ#ixiaL|-5ts`8%TC$z?UeG z>2ur`_7l%Vv{lpo);3$iJBe! zh#fVhB+IM@;#nKvF-F^I9BF6FzG$r3Bez_mBQ0V-_Jl5mNKu3L;{OE<9i*FR6g_J2 zj?d7TxDzzo7k!40Xt$2gidV0GWcf1jvE^&mRj+10mOt>IT)krDs)rt4U9)EGxq|J)i^d??oDI4u`SEI-NeiAYkkPe?z(x_y|}Ah~RPK zEyQ6=;tf&lM|!wun)GMlUW<7^5CJnl1_(fu(WFOtG`$t|)BrC6n%w^*sL4eI8^K=& z+&Lom<$@8ph~V?!p8+@kSoAOd>cKAp1^=epHt^pI$Uu5Zr_%y_S--^kBnehgza#_X z&!Q=3oZ@q5oOVFAopCyV{HH;yM;h1%>;}ZGz@{_KDx{0RD?X1izl*V>ixGQE5PQo< zxDiKp(Iz0Q!TWzqB%Xpr6<_ zfcl4i=+9Vvq&rp*)k$GOb$x!!Xmx_l5Q|laOD8~C`Zz@n8QUhpme1Os?! z9P_v<0+<1BfGVT;f*MTqQ3I)N3U0EGLK#IPp-jHee$ny~xu~8m<4?z3lm%ePumv!g zU?%`w;#VRX4cU>m_VnOM^VgT@LlKvW2V)*KPJ27pSiHZCpW2WPko~2%-^G@1|{|5?D BxW51Z literal 0 HcmV?d00001 diff --git a/firmware/am335x-pm-firmware.elf b/firmware/am335x-pm-firmware.elf new file mode 100755 index 0000000000000000000000000000000000000000..15b525168bda36ae277239d92415064e717d480a GIT binary patch literal 217148 zcmeFZ33!#o**`qE3^Vuv<9IjVaxaX&GVcCYWrS&-~aW!|L?o53Cwfu zx#zy`x#u_gJaf+VXN`?WDMkFx5M?4B8w6(II8z8q*n>n^*htG1X>NL4N{B(_POy~` z>82Zq`Ba(@||5LOv&u4|Cs0PeXbd(i!-ddLn(|o`o|U<~N_z4}FuRV;TXC zfJQ(gpb^jrXaqC@8Uc-fMnEH=5zq)|1T+E~0gZr0KqH_L&ol67jfMUl3cji8__gX*PVq*H93f+C8=0 z|DLzr+<${vLxc-!A@!2mO+-YYu z7L6?){n9}@r>Op=gH1h8je=WZHuh}nJ~}hjz21%`1*3(NmZmMuhy_~;Mi-80`m{<+ zh^`T4okWi7Vp*fxw&jkRUD(!|J;on1`}kVci1aZp=gR#zS~FsW8z)EqzAt6W8u4=O z#E#M$QTcW2?tQ~ihH8Wj@b6xP#}U>bJc>X}2F1&WIQ`#>&+%s7rv#=@@_LwfUYgCg|-QA{^Qtj)=BpM|bTLJGKFb zDWyPUOIQ1EyQ+2^1CR8|u9_Xb?GAk#=wj&p;OO>GeKn*eY@^KeU8LtHxMyt>JK%FE ztxs}2o(_+{RvxD)kJDO2TLHXXr#!7xp7uGK6qaot!Pl<#bCH+h-vC$;Z6lBF-A5mZ z*|)=hjb`MvO-7yBAq!U59%*H|Ts*R{Xk%V}Mu)5r<%e5+k;YLSTaw0lB1N&lxG@_W z3LmR2iTZZ<8d4g4J4&J=+_Bvr|5-tGq^tc;T|*=PK>0J-M#2bbK2g2JiipM48+#)C z!>-H@nJUUUTcZWlz@}r17io((&fb_386NpTuH5x_tsgb=TUV~Vbyq>hc3-4pdliD% zk+M0vp=wJ(Y}tkzYV8k1*x0cB)2bSCL*u9~4~A}<|8#0Ys6H=tPDeOAwlHOMVN|r8 zam4djUZk$jUL#i%H>)lHjDFH})4|a4aO8x(*fCl9L@xYpLS5d4ARC;%PJZ9Nw~ZI2 zcI385@5ssxPef)GSi3~oLU=mA>*>g%NOp%zO4)B)JeHnke_GhqCH6{7?sfn9WHzV*8OF1x?Dw62ak>A==Y&Kq zCu^T8Uw8R`0d)!J=?GWI>*V$FPEf1mEAk@vxL3C0d02!aX~4Se$APf9;xC?(Lrt^A zfe)5`;H^3E_oYb(yk<$8*Lwk``?rfO*!bXqX~As=@=|Z#F}_U% zfnxB%{Xz4?O`9XZ4mm6%BFgu)dLKKOZ!{aLk<)^!BG&4F%FWwiuJcuhwl9~O>q6=? z?~qtvqu$S2YOKp^Yx0{1q-h|2+tR*hAzkgOk6Y^|ZKS7h-A-4x+tO#j6>Z~|RxO^e zv8(-u$5F@07(vQ9TA6a@p`%a8b)w?TLmf{TYaiTi_{qJ#{lF9c16@Pp88v9_GmbAP z>}(xdw|8#{s7>G3xOik+joB~Kz84fc?-%)MZ&&-o<8shDdvk4ZVEWPzJA_C&U?qt% zxnk#6TbDL2?ibnRNKfw<`H)mw6D+)|cmllEy%~qZoSL@jkR9Jve=s zRl#2zs1p9NH9Jg5h_9Y)>K6&_zdh*RKQ@T6{PgjJ#KHGRM~H#EFSui7S}-dn7P(kmuDOAtf*ZTk?$FiWf6IAf#+xY4Wsz8zb+C7UTX3j=o)s$#w0vh z8}}W1{y^86?X43dqY-=Y*q}PA?#~^*4LK^mF!HN_@9+pMdsP!#_FGMXaJ+4^U90xJ z;>UY)k+!_4q|IlJY_rVDeGl(Xk&Upq=9uhl)qT0gGrFt&#$&L)cXXs*gy@gQnD{cr zltTz?ha+0I9;iyqZjtM5*l4U>moF;h>ZCuL2fBu$^$exAkz>y)Z{@neIvee*rtPgJ zYh8tEt40fYhX0Yh|6aTIN!nG5McJ8$9{v?|*6ww%Ce9UmE!BR1`~>U%%tN<5@q@ke zYIAGj-upfK-(Kte``edz_Pw3&{r)@8qkgE9#u}+2RT%yLoxZAHLW}GdxfoVQqMh}NOxa_slLL*_p}odB1D`>7`|R6mywAKn)wAX8?%wfj0a5rD*|zUEYNHJD{u|K47S+mo`bA3i zT;jd{pO-zk&Mp0_JyQ|CM8%y_-?C>4I9ETJu-e%6NfAbV;eT__=cwz{&0@<98?47B zby!Jx5xKgn{ez?C2CR|xTbQ%Nj^k(-DfOuhYipDL*g7(=E_rlOn<=gSX2e$qoq`)` zYw_%C^^NwAfmLI9?baPN9ahER4>3nRFLrPp_A=(DLFJ$@ckM)=&HpO+wxnRKFyyd! z7E)o~c6s2|5kLFq?ld1)UaR*29>xqrO&@ZQaqMswS-o zTZd@P>KgISX*DKvzAI-QHV#tOsIN(@?Fd&jj%wO1cKt_ke-*n7tpC`rxP}aU5sw>* zxNl1cGx*D0p|EJ{JYqED$MU0GX9dN_b)hXikoI&}S9{gruJ-D~k7DHBeb|bOezvQ9 z|KSaI)*asdX{d%$Ki*;BS#=~HzZq+E=qA#y-w{T;txTj}vx957BFOxxi>tUIjU0!#p?s!m5Nsm-T=ib2XCh0Re)EfcopDX zsCbp&byvJf@Fpr=U+^*&uP=C`6t5q60mbVF-sy^W3V1^CP62PA;#Gn7^>N};1zsP; z>kr=Niq{{!9*Q>ryuT{m0PwODZyH(4F>P!<6Z4%bOMXc z<*4H?JF8H)=XEk)rvB^&)@?27(uv>u0_*nGMEc4X#10dCwftB{>_hwL+w6;l=sPFz z-ih-p$W6kElDiVATA?F*g3p8oo+OXDaTJD7aPm<^Wt zbn2aXuIMAg@?JuSVyhuPIyJgB`av{*+SF;Z7e+em1(m(vN%s5={?UOkf%-Mm-k+9# zDeZHN@F(JqedW&1#PJP)zxq>;@W;|) z^);)!vW0u~4`TPml47CQl$sD^-Tk|&kI{S3Rq3yy^suPHLUxjRH#sx|&l zQMf@>oQj>U9f|LC*IkF)DlTX(LyobdyO!@fc!v;e_K|R`UxQgw@YK$O;}QS! zVU8;Oy6gx)r*w&;uJ+U}SyYBtQkQJAv9rxVzP~>vk=tnOcQ5TKS^S~b_fGeQg2fe2 z9p4xE`f8*+d-#KWqV0jhY;(D)&Gm5G+-I%qK^J$)vOTRC^#Z+tvvEcgW7c>5Dr_kHj$f0M zUlWhS(fUVcIF<>IUUxlurIW2B$MtB0@~GpOgVrI1)Tzh)@W}*H)!Utx@p$Ja@aL(+ zt^2<1gKx{o*n4ifd{&LHY-8CkRBu5m4$nnj3m;=0`V;;N;b{|FOd-B-dRx@_E`E*~POK>TSmCE*9WqT6r`e9cpo2S9%qJ+(} zfHn25a`hk!s}q#osnGiZh#YSZAFtU#FGSk~SjSNQ0#|;RlCOs3A7Qup7^VH7&PwEv zx!s4exziKvCAgjXLtXs>rJoD^OoR=`ssDFSJv&)zU*o(iRKv5lWSo~_){NxEMzl3u zl^4ZnqU3B|TJtf^49PD#!N-hFz9{x>ac0`C_OsNg4Hcmh$JB z*6HwMa|>7e&vX)%5Ks7AR36>O)#hVLo*q7-<{qvXfA%cb&i8lTw~y=UwVn51?x{na zu#Vz5isI@m#SKN_{3!ZdCrh)olPl?;bke)KI=Nb2*-7v2>|~j4>ttz`cd{Hebu#B= zom|;o-$}2p>m&wCI+@?1PUgNq`IR)TGS-Y0VD)x?eaNYM)Ev%+IxAui)*eiFe9^OU z|5&H&8y{l6?uoFylk-CoR=Jov+HOY==xYD% zJ;$?q-xku>sg`Nz#+fuu zelhCu{I{!p*A7F?GzEJ(HqxdO?~#ae4V^NkZF4I6Wy;y9<5I_lM*Cw!VyO*%W2G?> zTUpy9dP(%Y=tt4qX?APUn525HJB`@#BPL-P3Kvs1>Uj7=SrI{LKefM`Xu1nc6C?a6AF+B7L6 zx*u(e{hselOceJvxvMLl2lI~Sdz*iKmz9F|A;|0Qu8eUR4VhyzM`w-6s;5me%5lc3 zNNO=F*jljLk6zg(E=9f#zG!|FX!XR~mMWDmPD(PObsfVm8qrY`B#z9Fd8=}{Wuiav zyrbZ8-r-EKy4G{jJ_7v;wZQc!>ykB|Q{T2ZEp{*7i~#wxtcI-7-D4NWvc~>2=866# z)-zf-J7e6<(SA{DMtbblTDx`a>?yNT$ApxZM4Tv!ie0U796x+7I3@{ur>03s(RY9q z^C9+eZ0LKu9^yC>ClU4(`7ws)M)RT^-)kg4Wt$Wu+N2~GqrK;-+NT8}&;6KDk+DM* zn+;|?&IPdBA_h#$krI7Dt!bY1ZD*f|9FZk_XN9+8;oE95qE5RWhy7*z-Pc`L?Lt31 zS*P+}>Tus1*I5A_`e9aBt$dF)vQ4Pbn|`oNC#RKsD=iG%Gh?Fiw3d?C1%;=!Oo+X- z|0Q!m$G{0<|4Xu8tUfkiWOuPZI>$dJe`HcDqxNX)Aucl=n{5ZYk z>BzbC#5%WQP}tu8vPg@)G@EmhoU?3>ve3em8)m1enM%&7J;4omX9r@Jz<%*(ULz2N zT;s1}KZ>0e>p!yiGijVgPG}Tl66v9u7-Is`Qwv{eZA06pPe~0s_wSTbW1-E){$Cq! zV;|RKhGXG2uN={4%c5xdyK?%&zU1u511I$*?8&hszW}|pag@K_MhxRn zRFofR@t(Bjr+#5?A%X~O?~Of$h$4ixBsJW)A^AWkCH3``zo!b{SnHQ1DAVR0FC84Q z;H860C-H6aq>fO~+7JRRM{#DrwFZ5`-V6OY=_Fr#?s>ospL((Cv=H;*bij!@eXt36 z&nOdOIKm)=_YfXOcnHDQAj=A37=y&5jv~%lSdj;J@SMZh^$|1bS8oYM{IUANq!t@* za;l%@esgyhT2zmQ!uHnc?HHTFN6_av?%)hXRBse&@4fzjAvPRtYOC?%Y!G&AC*95I z#CgiUN^?#Eo&05k3||m?ejnvzg8_O=g1$HQq@wxm4kht>B32|;KfB?N9ir*ujt#q? z>JU3;jk|30Wn*6G5WC9Sq_^+#n>UE%{^dE*9*1xL>Os7FuvhgseCJozs$XteusE~B z_`bPn*(PJvt(!)DX7-kN3(*rhZoW;}eO&b2y2BhSPRZOcEFw#O6}tP#!Gq@hx8|P{ zn%F}exTC47Pt8F*i^d;XHh*2dcbGV^w5hA2=HN|DT_qUV4~bgLqSw@pwwr3lHC0zM zt?yVKI-_C4nEWPhQ)<(u#iG2fr8zSCEA#S?J37`Qthh5Wx5^qi{?N4x@m4b z+4ady+dF&{vc_iN)0)r|&&--aJVRAQ9XAfI?Re;RM?Y>cn6M~Hn)ZnzyZGvTw`^Ll zZ{4OPXe*%&yXqc7*nqGZVJkuzLM6iE2x}0MVp9tTx3q0eiHM&0^*OQVv*&k|)X;u7 z3N%oTlGsZJo60`&yNA%5`#2bNt7eutA`wbhTfE z`vSi0v`rp+Pgnan-{6Vxz2M=z=o@n6osrMAQhp(I! z9yxAcc*3}FWcK_ku9-G}W4Lih+_20|DzQG8k+v`KtIRsGS9bZOep#krPf0watNLZAZ{f-4xbk?zvB zBcDcuW(1}8UnnX)S8)yGM2;|N6%5b3-W^yY7pSjfBd%`@?nI^|EAsw$X|pI zL&!|sRac1c@9HI9C&}=N^^$@>Jgdt;t_%P8 zj^2M(SL>0_LkN!}D2M;UM-=ii5a#|j^}>)ZMW{qzlO;Z^6AOX$!Tw_+uzr#d;{A$p zq7Qro_FxOK8lS5WPzT~R1k{H>eW^4)P)}k70_%r=sLyzQv>nffd9s@@uWz>*l_&TD zeQ^T%WgP;e{Hp;SWIsjlArz`K^cQ?qB3M4ivK^Z^D?$)Hd?GM2ju$L2^J4*-H#;}| zV^>B}qAL>%_2;H1^jtgtPCgT7QH2O11->8zgYXjpw!+;Ui@pNqQ$3sT>)|! z;vqzn$Wfm)A{(FoPM&4RM4-NiLet7;{Hx1kHXm-|B1I|a1{?iC( z1T+E~0gZr0KqH_L&4q-Za~+mE1qtKglHlSAgl(A+C8``)Ej z&vG@s`*ABo={KJ8W;}ym!@|w5nv3v}Yu(rl!cMP3x+#rv$XnI~MC=UpTw#*#3APlb z<&AoT@Qi|nk}-z9$~YbJs4K} z_QjA%vd;!5XyYN0?M!e|>(X%Xjz4@nTNEX zF>xQ{eTSKLj!Kg~gzqx6PGgdahEj!ZBpp0YMFFF)3mzF$RMhBh`3hjsxWLivX8C>( zd&X2JT4?#kGkT#DEwFr1_+?z==;m3zN0{#=PBh2z^}$3J$%CNGa7x!EbtWhSCKCQ z)9LDbB_onX!(7m~VhBMZ%*H%|m^^>sbYWb{Doma~?HXY$Abi4$9u4V53uy8z`gP6u z;E2E=nqN$|6&Oi+$r8}Mz&)g|B^?lO7N4f!O^2fQW6+t#4|X$YDf9gywMF0!TD{(} z^dRY`ufX>O#-WuPOFKaa16%Plmi2^qDDVYyxPf$9AV&EcNvDg{&mk1z0~xb)DHiie zOR+lek-iq9@@8Tn3t%B*{>;k|CyA!+n~0{Ixq>1qPj zWJAqXBJV4NbM8VGvIn~o`=)IED+F?hk?cW_XLmel>~2@klIUR&qiSvz8M@aTV3>o&XuA#6N^g32lLKv5*^K4m1UZ|n zSpG_pb4Wg+$g4;`NwODnr?Ffb+sMwWJZMGKNnC}*DkeUi3;VqZuamGD38R^?z5h28 zUPeNc2`|(q63V^;vyYr$o6QE1EVu00^A?KY?Z{C6VKSU>ikE~o)r&xqwU|a)n_)y% z-X26%<#mE^q^3e@4W<70Vq7W(87Zk^5RTL(klH}0w?2(aof?;Fpi~KSj6&*FO1-Ue zi^e4uQbL5;7MmdA)Z9$?S;1x~C#gor6ch!Guofu_!ds}P8sY6oWTD+gcwgM|MrFB} zx^g#rgJb(WSm?eNX>vEKlyFw6e)Bih*d9bxmQ+w#?%5L0(ge%N@_tYOX8EZqOb((7 zlnP3S;vCqO`yWNb$+E$VEaxaDvpfR{PL^LV%kLqovZR8_^2;(Ps8ap_EGNrNpj4J$ zsVr9_s?o}=*D5tHhpL<{eBM8Je8iD zGKK9WnF1DeM9<2L(IB@vIE&64WIWsMC?{P zLT2}8!164bIbWDPVjzWQ-qlwKGfdj@a#A&mDB|<}lyosC#*pwa`L_suKBRzatRlt! zGl&4$!oH6=n>`Jb!TT7>X!cTBcsUlEy~(zO?>tDRfpm3tLZ{L!9|w+S#x!A8I9|=2 zIZv3CCwbMEw9lIiakC$#gN~_-K`O6$2>bU)tTX$s1D@w5&~uX5JZX( z#9k0z!&a)CU@W{|Bo~5rfKp))M@f`|@FFCa;gODz!g;0}LSH09PmUKU#Eq;G4$JTj z7Rl9!QzzvNJO(0M?8;Y(lzK$TW{0AQfvIw$ZB8j=>>M+IX&}TJrgzFS6e8_cEaoVd zCyFH1`I|RGAbA#|#6zSn0!-Fmwxx`ANSi~6xoy6T9YW@Eq%qhPnY)n2U{_{t!Uu!h zCzG=SgJ)Id4<^HF>-Sf^!H9E)7NnCK>17Dv(A^v4gGCyN(8zJIJ=hHeDn!>8bQ(C+6G_09;n$~JO zEi1(bhpZ;h9%~q$UaJaEpY=GNe(M=L1J*5gCRz968MHF-Otwn##5xeqRQpeOUJq?G zU3ys*nI&70r%%lgS#(19x8gzHohmqvSm0$m{()j7%?F>uOi)fR4@!)p$p=veS-2Q{ zHS0HD4Xjd>xk8%b6rW0qTw+5M>oKlRltqqhNk7En8x})90*4Dnu2YbY;VTwt-1u+_ z<;F$wiztfhMYh@!Houj~wimPIY#QQHn5J2Mafd_AVA49r-?(+|6~1AegQPs=QLv}b z<>u!QPe*s_Pus*m73mWY3Z%1(kgkff8LXtw@NkPHtKi2b#M>b;7=c+X1dsCp-53++ zT`w};1kXUo_#+<42%-1zpkwnKDRjlqUtHEQR}oz?L`QNF?smn7iO?ZLnddKDG31$a z7(C|b`XDmDB#7sr^$KuN%q(^=TXnE(c3lReIr}PzF_`7q@s44ZWyiag*(W>RpR7=J zyg%7PvmeI1%-|^#Lt)+$T-v#Pq#N2YXB!K7ry!B7!x#XjERtqVFt~JfoD$rnTVmD; znnNsJ_R>0!epF}(2QbHk)ohafmw}`_b`6CGAJ zv6q4EnT5wi$jFN(cnzNC*jI~dxOnn8WwIWpnJg+UhG@Pr^9s!@SU4XEB%elt|6a=F zFuEJjz>n}a0}%{7Z{g{J5_|)+^$10~(d1o_>W5F+Tm~N;lig^8JAve%Dmh}a08uB0 z9!y=2XyA!>4k;Tc=N(E5W+qlmN!FR5k>zL)I^{~HDo*Mmk~R}VYyb@6NyIGi5hUGK z_#U`yg)hVYVhF6pCsq_}J%-bIOb+rg^aTG51KD{TR9Z2=5s?VSv_ay#ljY4L6kg>6HMxXvlq zMEJ-m8;%67CJU(j%_`#&nw8EL>CU>AbqOL?2A?*kjQ9~66r-@=>Cb|n^AjXUKD>?7 zumwKnevrPac$%t`{HhWsQviKef$PHR$)Tt8aHS=9HV+n&YTIhCs?m4ZY^P9@30b0n6{H>2?+tF||lWFIh-*}A5# zLhK&IQj{;z#}GAFZoWPhY11gV8EK(q%v(iMr^kf6RFw?_eJX}P73qcPEmaj^I*afF zMAK9;W>B;u38@ROfJqho64A66O{(aFXhdn3F*=jNt;vX9%IGYX>2ZwuX+s&kjAefl z(NqO+jKUTU1JhI=n%xu8r$cC;w^DdXMo)eOWH~-E&mi5--AKl2k_B=O=&XmKvJAvI z_>^I}3iC+zZqQw6Al?Nr2(v);>mbIG*a>19i7g=3gUE?1=Y9^(AHd1|Cy07=5|lL) zWDFp|Cm~13w`siz^u z=*~hkp*{G}h~>U_;|2!9Cxt=}COFEgAoh&{gr@)xa*DEDa1KJB0QvfgaE;3guZQh!OExoU+VtQ;HfvTn3Sg91+_*R0{8l=sqp%gV{&*+li9h(OB77Ucrt#NwVi+UZ`+}8>J^F6fB2o5)efR&6N{8EaJ+^ zIFh1Y9I;iVr6OekJd(qB>VbVxDST#5mGKeP@KkcG!eDwUIXN>JzDiF042DmSqRLeK_Gv-?6`YHvSHJde^rTqCCFh;BL{s2$=*OdAN$tYTh z=Z}e~_m2>KAABrDD)IF1V^XRjUnLn*_IE}1Ik?V zB)B}b!BEH@rrKp@i(evc30AXPNFU0}LVgMstLSWku^vpBLY7l3;}SA5KR>}3OGbX% z3C1uo!iP>U?jWOA#|g$#GAfUrU^J33fOD>!OC=fAR)Qf$!ZAxsgo^8JBS?DdgG?E` zeU#pQ9_L030QaHnPjEl8MJodnQYC%GsWtMXR6f-$&g(N`os?NNyMbeo{p*kvw0WX@P!Z^ z2LJt7tDFJbBPm#r3oibnd!!>RY0)&SunH>4m=IKN3kq10+c@fZpF?&s%*4`!7A-o0 zVG8b~VDh~%&0zEse8**pe8{^n{snx(SOwM^2y-Yg)WjMhi^$66S$LX%po1YrVnJXX zWHg=8Qbg_JjAmRx@kxp8wX?P`xJ{A;QxfL3B+M01e_X;`H%8|r%yoCo8QgivUQG!D zUndOoqQ*}W2Fe)SnlR8OZh)sEvj3wn;M4$LuG;U?<$tgu1QyR3d%LXuA~3*}I_-p1 zHCQ}UI@@7!@1IobiqZCXG_HDhCr8$Si=_rIS_4^oETV*k?Gu8AuSg`F2cC-yvngEantAg?{`MU3o`UJW#(6jxTm~NTu$Q|EUGYTl?sOG0 zV4l995Xtwr3c{RACoXPA+;RVWNZAj$X4pEE`z0dj3Gfo`m%#lWP;$eX?dhXr4vQ<_QVSY1EwJYEFo2o+6Si zJz4Vxtiq&lvSt-DIabJDE>38^L(L{vlY4gMZGVw;>&cq^ zp~>+~ZoECAnTkQszRT4-9@iWolI}lQa{x3qQ*-P3gytA(KI&>#Bck#gD3UgvtT_;x zzoF)i%?ZsLsJYG6oFCU5B$8e^S#uCH_fhkeSHX^`u6_zTYm2M-cwBR^@V^b78vWh_ zp+>*IfKa2~Cm__I_YV*p^gcPEXCDHOqaIJz;Xg;cR413)jo&>Tf zV9}Z@B2sva=uyXrsqs@nqfhFcC;@|6kcx^^?}4qKr1HHqgS94=Z}k|gdsFwIfihTY zQ$6gHl&CENmr$w? z*95lNfh_zy<`Z3Lla^pVcfmRUl3=Yz6+Md2JH88=x{S<|* zvWgEN!FxAYRtfcmcOOA0Rkpmx$nQzo=Y5R)UZjKG*U9fqI^^BKYAGX~>0L^GAJVzr zE@oa%y3o6dbOq_Kw*-^8RY|(kdj)OwC0*t{K;3?%D}~vPglen*BS6f%jR^yoAiUkF zpk9Yt-r?jABH!obk(BdlGw2;l{t)s*-YEH}lFsy2l3z_a*ZTyW9!k27mDwF8S~`VxoG8SC*1? zV(O8PP(5iO5&tG^Z@1glan>P*sY_3yrC& zDpW#0oN=Zy$1|DZIG%W>m{egNv;(pr1Ok3R6|m zF1o&%3HfYTOa)RxKFuXp#%*A=z(7HqUwAh-pMX=i9z?rxGnXt*81AmXhOzS>Tr43hU=YtM&?C+7yv>sQe zv>KJRfN30hi#U`mN358S#C4$_FS6i8iU!QxQ$R zflw++Qjwo0op&k1zs93v6QsP3LJ|t=RSG4GA>~a!7MH@+dybxzb(a9VgU=5@TOG=^msl-vzW6ChhaBA-{-p(Dw!DV$vbsZ%LPs&h+)Cd@1Q%UvJVqNf-Kl zMY=cXu2bq62>_(;4uOu7Q8 z!Da{=>0GVYW@Aq@Q5Mb5mxv5X!xTwes0C+Ibo{kSG)hsnAh8I+5+l(dUHpF!G2)+( zg{`yruYrgR@%djY{(1CtK7xl23Ha6fO>YfiKCYRKG@OpW$>qIJ|D!+(6XmtU11EC+ z9DRv7j|S^bi~>B5Q5XM%Ctv(`@Aq4yAZeWnJI2s0wEVwwzu)8D?{7txUgv&4eNgxN zJ!)xcOyrr8mrGOQ9F>Npsn@yRucBC*dY$|ID$4%ArKvGRMX@yXa%pN@;OKU;($t7L(JafWu9r-6 zqM4SLOH<=gCz@e-o%{VNilwR7x!URTy;c_Y*!0BxD$ic=_0W7X7*g2jZYP85)bpw*1ch z{wQdl-?`sUI)D>+dg9*i_k0WV<=?UO3q5g@dhLIM{iR3E1j|gRNdT*y@FYtzJ0T>V<=?UO1#VFC5aH zHH)P#)G>D}eK&nj7wT+vq0Uwp>TGqP&Q=%d>=YkFY;~c|Ru}4Qb)n8y7wYV1FmwlO zb)n8y7wT+vq0Uwp>TDlUQ`0yJT#sckM|IY|9Xifz)i%X;wkehx%;}RSeJE>gz)%}zu#`yA-zpa9wDfhY!%GY>>x5U(T8T)c0r z8D5%;_l>?aB4;6zs*%JVge1e)Byz5E#SbbT6)rY=Uq$jot;~0uXs{gPIs6 z^X_+fh|4qCp4dAwo@0?Fa4CHhQQ76f%#GBHfY=6t$9j%t&Q!2|3)T{s1)sQi{3BSmCs_IO!D5y_by;VM z>_uSx87$!6if2YX(Yu$kcQc~0nFneP{`V69iwLP|QtOESGmI+yn+f>u7*+V+r>cX1 zzW`GX;2<2pK{$YeAmFDUj|AZ05%7^G@xB6&fS;#$4&W~9N>}Uz;7CyLTA1Nk*4rEj zz?;Kv$BfrIECnC-{5^fkyK zUiy6SSo+B)@xtJ-^t@oij)vA==CYbxv3Tio`XPa(;T<8C2<@N-Eau0%4PZGqM8H%X zp^$#bY&iuCj3?*=5+}sn)UcW@S>k>uMt4=>T*)GjXR&5MoTbTY1QCa?KbFY1v-$ME zc2PKYl9~@D%=J*_<|oV*DRcKTu3AvBBx)NOS52ry#s7eK&UG;NDYBabV=UG{k-ZEo z1?=r0=t|Bi5X5tZBgA1hXDxWd^P!V?>%k+Q&nTY4^HG=8;)*5k+=_&Qko}vJAP0-Q z7r{EY#1A1Zhsl@m0ki;ME@t!|3cz8i#Mq{W@5IgrH>h5P&^9GB2#jtqs*aExuK$W+ z7U6Z~NEOQ~1ja2$J&wF{+Cca*w9bZMHWaMyvyXz830}4VzbrwA z7_0|e#f1>-1zrt!Pse%Ji=5%E-0$K%$kn=Xyg0y2p_?56uK~K*^&mKh=1c%F4TOo} zFw^lO=RB9i_jy#rt?m3Mc=WB@6&r>Y1lC-zL~$v5+z;f9U^oC4z7J9^m&@_dsL<+( zZ9{jpQ@>T=b0P700Ljkddg;j%CtJO|bqu(DqO z@gRv8K|Dd?6%fyY$aw<XL=}&3bDiRCP{muq;vGk_DxL@8EFND3yO{)C*0qUDa?-(~D|fgo z6fYYrRjPatsxV;?EX-4mkSYw}XJPVqfJF;V9-{D5jtsw(Rbcm1ZeEBzpX25m%FUn9 z%^^ruZk_>xZl*xWb+gW86}c?bLOoc_WRS~(n`eVXH*+R~pm!6UG5G=yIj*zP5SHNQta^3*J zB5X+*&3hNDBVaL)fw?@3j})ggqyv2V&ogl!MQgx^9@lXzfx%2%*|2RWq3#c z&`0#X3R1@Muc6!fMeY=TObg}Luq`9_BIUd&#o!-R&Z|%i{xRje2 z_HYKXqWpZ!tqf+L@>9?d7<@J57bB6ucV@YX{2A=C%Xu2fV2>~7xh;cTUtSH@8LYnL z?Dq_ws`828F!+X-^C*$Q9$U_LkPP1Za=zbW@aC5HMy3qDk>x*x3WH~0`8}vJ2CGkb z`~|PErkvjtWiak5-w8Pe-|6L?b{Tx5%Hv-dvC7KhFOCC!%Hywc1LfuMZ<-{9%H!WO z@eM7HzZVXal|P9RF!<`qdAiA9Pb^PH2^j2#axW4Y%<}R#kST*%Sw0mX44(ewet5-T zRg`}T4ujRNya~0*U^JI60*ApjqMUDW8N7MrKgS1yH>Z3B5*f_Cg>xi@g=l{0MEWW=cqKCeFb>-<(Z_SIQt6l?8}q*ZHoZU zzC2S@D$c$FSlS3r;Yn&* zizJ_?@e^bg8$~OU}j97v$NO=UUPMd_jz!xMyE!&O1gr zVcv=4bmtu-;~CC7MnyXB7)grEAEKo4YDp-w)P2Nj*osAFFZ4>eRPsAz4*v$l&mKYf z5mNFic{%DtVWf0Y&S%PaDZfgjOmtFmX@tGo*+ZnFgn6%nbpQjS$fcpSL}DcqdE+Ik z8xr$5xXEEYSf;FOo{Z#zY(zi8)KJ(R#6A!rpH~ZIT`A&YmE0qc>&C*%2#Yxk#tKFw z^(#mhj0JIw!~_sN3`Yf%L1cg^ya+^h5TZNBeR&>B)(9R(G33s#D0RbYD%E-~fXyBk z!Bf7MLV}P}!^;qz3x4>TIKeNFQW6$9%*$!Ccw@pMf6RdvRm)ozx5yV<%Hm9A@$R_A zUyz_hN}|-9-Q?Ug32Q%xwW3Z$a715z|Tj}YuaM7i6HbfAT|cFOoc+N8Ul?yhl4 zqF&g39*3+0{L3(%w-&5Ih>2V_msgP(qM_#9Oxy(4DNM}g{3}7_0& z7UvBVrSn`KZ+a=+zM}M6m$xF$>nBQoi1R8%=`UU0 zt~hUiC}lb5Gv5&+Us)qcKX6%IAfa5T621Q6@(SX-L8A1K%i}SSk}DOxkGeb+F9E!q zbLE>MoG3LDyyjPNhEc`}X#$IPX64`cMz0S^RVzYf7&%T*zd_`xcPM=Lny6}*f@j*uWX zfjkoxQt&hgUVbj1=(!|b1rY^N`X-3WLGT<1IpfTx^lh*ffi=lxq1}AovSzs~lzx}X zT5>{hugkjKWmSptHkU=ySlw50$tZpD5ZE(Rp>9G!`f`Xa!}@vy8&{tE5c+*cS8$*% zs6z_Jba;CddQ0INzb56FEH#iy7Eg}ILIwIgIwfK^%w;sFq|Po&Z1Ux8IH0K`@hg=d3!354j!g|_Uk=BFuOo~8=hACtl< zoLrH@+4U`SoT_;oxCTn5VYU$c)%TGGDb3^_0OC~uG2k%@oho}m*K5Gc>JV7(7k*kM@;#x^&T z!Jmtf)sDngFa~pn=eao@h6g`~hv!a~eMo<}#o!$^WC1c^u&Rgr2_Fo;vLP?wgTd@Q zg!gC|yrYLK2ZzBLI)qm?8SH`~@$HPiY)E_;>;c0!4L=y>5fs7A&@uZ z8E_Z^8AIY*H9LO@H)IUfz#;L?h&5z8}cSN41xS10e&!~ z4avm^Lm+3!T+ELQX8Dkxfy0m#8WP|4nSBKBl3Jnwe0RSy7H)FCvjQ8jZ(Vp$dzc3R z3!=7rLG&7mmN*wgAEoGr5DoMvw=xIiw#^lBu9%0W zV~IOYfb&NeoULfApQ6ouLXh}P=KtQ00VXzs*8hdgpt~6NXJIrfl*OmR`@m_SWeG_U zc$iR>lC}b$lI}^`7uZU=H|b!&NBJ_+p+JOmAJUnD-%zHUbgpoJ;30{t26>7M{+ORr zB`<;`tF^D%DT$OiFo>r!qNj4smuD1W<24uF$mX9SIi%#zWdD|v(u$ErC6PWB&*g|M zR+8@_mN5m7%$?xXfX8?DS>#N^s3+3yj4P(HTgq#>cq~<_0(F_4k0>o>cL%Wmgh*wF zlFLdFzg@|dK+bXIM#mYE%1$Zo+W?U&B{CW!Im+LkQe+`>&Si&}Pw#?ISP7-F)5;eF z*0PwZQA&QNCNG5Og=lh15y@9mUO_LgW`X5IMJfjo`MW_0z1OH$=juIsLT~D~^f)BR zKgGa>56F){6XSTPewl9-y-ekpA@|;x5XwNtj?kwp%EJ^Y+(-}legx7sM`Nh%#?jdr zOo473mkl*gcl#Oxuz4F7)ZO^ATjuQ?fVR|Mz?b0qwZlD-78_pxrb%+Ann}4GQy;t zdss#hY3Cl6QB2yohh>zIcJ5&r>VC6x56e*Zo1J@DMsLbE_pl6gr`frOW%ME6xrb$_ zd(6%~EJNL6cJ5&reJO)`Sku^1isU#r9cWcFmPq_QCbpHz2NC2!pVP$d`qOv)FDr1gInd6i&`Ye4Ij`UsllsBgGTK!z`GZ;^M3Xkg%!(Wiop}ct; zM4c{j3QT3Hg5Jz*UkFo(lcikQ1|g^SO-=}{q|nC{%34mp*DA9TB2lC-+=%7?cdD1P+&smtE6v^hYK>lhhL?$bdtdE%2bF{G@%xcBt zFZ|>@$%Jj}QpUscoGHi*QyP0wK2}*Wuy!)Di>9Fd)rn=FdN8_sy zx{GeS(`RMyUUtFFc;wOcubd>_;j=QgGU)+4y4?@sb~=BaM~Qa3it!At&sDm6yYeFp z_HOUy0-wG6Tmby9dbMhn`gWyoZdVfC`0dKV-stQ9{&ppikKe8gQdHfptU^Py#1)tZ zUHpTn3gZ8B;_rNK%B==dovzApqtGxcp03KXF-$n8tEM_#l@mD$t1rFCb5vUVOD}Sg zipIb6BF|G%tfkeLUgQ)NjeqGys`DpHx?g&coVnnh`=u9op%Zn#^dc{Ebloq#$V;54 z`=uB8JxAF6(u<5bQTIzPa+(u$zw{z6b)xQ< zux6ywux;`;#smCyNn@VF;E#0&swk1`F!<}@0shjY(br+DCu1>HY8(cCS3EEl)2%Vl zVep5?1N^-+W17R@FNz2F3zWtW9R`2GJHX$eG;Ven>i58N!N4tFEF0o8vfF0f!}u14 zzkHpq*1(>V@5MSVL_Gigg^0R{_%_h+rt|r~a}m+&TtswVi}+7Ek4{EO0-QUIEG}h| zPMk+4opc@@JP(D(Z@E-aTK(8TC7pNHv;C+jg8!h%VEjZ#Tx9SG5w&L(DK}B%46@}C zh$VHgDXDmXUr;>bd>TsSo>b-&=E;<74$)*~7z+dyJr{ME#)kJo5vJ97Amqf14t%5{ zAyc);EON71v>{LnQEfyPcYHbVcy1|T9~VLDL!=Ie6pjRh?4QXElFa-X5i@N+o|NHV zHj5q!qs=2CM`jI2G$$U<8%3>2$`*NOenOs>MO#@kl|@VZ%fE^Uj3xq;!+2MVPua}n zYk`77C45zc)#mIO_;I$;OsOr#1fxzj>Z!JbQ7kMP72=%~0ET_e=wF7EJ z#Il$_Eg>iN1bH2#@E_S7K`QKnPm#T}r`AsD)sVLYyb`{(m6tR_-dQy-L_L*K}{;7ubWv%pUyEZ34CG?e6D315B6>086` zjCt8tWjx)@n6Trz0AFSNM-t4KlKd;- zD{C2hJB)UWD1PDtRoL?*MA_PaEKc}D3E#%bD|RJ>PNmSNCkYLJ5MQmz`JX3*cr~`? zp>GQHdWryNUx6h2-WXORVQT^CB6g%+zhYVrtKK$mL|U(bWaK)GO1 z0$$_u_{mCto^~F_ql~S9cVv( z{~qI++3~nI1t42uC{~~D*mx8IoKJac zsUS!fvd2$A)&r{%LU!%{{~%*alW7iQ@BM4Y0xo21d2z@d|F@8_J<2KvvWF6oWuO6D zq6g*yx0ThpkbOYNMq*gEP6M(s#Hac56h3*OTYc>%igDUj-@%o|e?f|e-@%n76crwR z2UnJowmkd}uIx$L=izs7WiQe}55I#edy@`%_#IqXMmp2O@8HTlq;oy|4z4UGUFcaw zx`K4r!|&kAO46kseg{|fC0*v>cW`At(v`wi-@%prSECJh_#Iq1fC+fvM}>i;Ef2qg zD+iJGdH5Y%Ihb_N!|&kAA*4f|DEX(7&h+p*xU!mbu7}^jl|xAvI!>QPI_x+-jC85v z^l;K;j?<@;u5_F}gLIYS^qHgw30r*!SJrN&)BFyu9LWT{ZKo4;q%Gm)*T{^F*C1n? z(LV5f1o&Ql`^+f$SaHk9WwrQ^AbtoTz(?W>OFp9F<(JfqL1}6v%%#RB;EGRBvzBN+ z(JcP%Hj9q15N|``v>d4Yi?6J4Q~5tWD!CGwU;(rSHhk&}q?L+v)X}X91mpol9w561|Mkn;7*j;aPg@V~nf9V<-7Qw%{>J z`52>r$N804lgIA)D!{%{EYzet_p+dO11wCD`QuN58f%$wImI6*ZNf*1zfYp482yAq z6J+=kiB4to1f!D&@$+T^whjx4{n>o{PPq%HDYxUtn>>V{+#*;osV5uoJM}*R^$>D5 z#PJ}ukoc)KC4Ma+P6qJ+iTB^8#AgHIR1i;*IP~w7_zwYb8i?meJatxy9#>$K*@zoK zO7#PfOsi!(<%Wi1vH(@8c&uoHv*it5g@-RoOUB3e4;b06Oo4)Nh9)8 zL_`;VS_BNl#y)%vg;N(x=OJ=CBAXB~TxY<=Jyq-@H!4>rh(>EOsM9ZiBL`vZu0zW4 zzJTMhEuj8&E=H9dh`fl%EG#?^g58Ke3G6D0|H3&+;@>IP zSCs2}!u6M6eH2`GlIxL~D9>z`EkM0n>i74W=uIXvvcHa0rCIr4xy82izQMaF=dd;b%4Es4i?qiECc^Ev_?eAV{gnNkA0pMIj81z4_}yTc z=EtyBK_qbu=+A&&Gy#}7Eb2VXZL{>Q?~BCQEJp<>XDD%pR677o8D0USlwm9eb~>qH zBhoys2EC6}nVpE;822U7CiC`bO~K;l-=Zz8Sz z!xJSO7wyf!r93;Ok6%;DBU}3TmG+b1F0{E_{xo6aNLeAY@sE(UigD`VR`Q(uOE66^`*#wG(c1ZD|4zzwfR{k{8q)3qQ1Cb+ zKSt^qOuhbY@JxIH(fqzoWO4=dyn~bye%Lq%|DgnXGr?Bqj`1`PsFX-C01`S*yt3I^$f?Gy7+$$u{rW-h^IAhwsv%|orQ`G$ZWK)|fY z0NH-C<@FYCCSR3~rO7xmVEjE9c_Dx@R)NYl*<m>Dx{pVl(cEI6m&t*=A?+2ZV<ytm3j~ETiw+3=1{hTn9 zCirSVPT_9@I$sLNnLIYf@_S|@_Fzdc9()Up#*ye79I5`_M(qFCjo5T&BlZ%8ce4?D zfLOZ`J45V`VOkg6i1AL5c$L|RonQ*th{Yzx=H?fU^YbsupBS5zTY%vvKGpN{uO4^J zxYc+<=HqJ2adn`>ZgOJdfFmUa%BTfkn>h~MIVa}lyOhy|f>6dh{JkJ6Z``;EhV*8bF>F4z~g_UyRLDV)Li7Pz9qr!V{;+XW5gIv8B^Z z-t(ugE=)+d3r9H;Cc7MlJ_Yok`crXbRRCO?M7fyv{OQzH{j@(>E}7yfgEGa@>!#0& zRTh$=W!zTOX2v+^coN3E%NBc;g>fi&rpv%m&l+VZvmHrbob#b0F>hk*zU9+-F-jt7 z{cVIY$eQVV{G%KrZA;R&(%o4isMbx)GC%`oad%v8Rs)EOcf{+>VqQT zQ1ZhlUVPd;%Ta>Mt({=$^e(9O$sSS%g+C zhc2sF{F>>#`9|L`dB=V?Upg@+_8Xe4S}#hK<68BoYzxf|jf(a+Iy5+fd0LDyT8y!b zz)oGQua+hcnr1vIYIcdE!U840ezPTiFqUm_lUz+vb%id1s+)W< zMX}YRii-B(9PQnvn`4QY8@BLhw|J)4V3DqU0G)f7rH)k#9~V|~yi@lHuEfA9+4lLN zU>eaS(Y_OCjm#5`blYL5CZ`PwK}2t28;E*_^<}KQTs=~;Q{R>z-A=_9PW>eMN>Gan zSgC7|YNs4!T}?!cY7}+$E9Y(D^@n$g+S&Ew#Rwg{VTtPYps=865fohPt~e`T>y zj@+<`M>|c7m7UBa=!;`QHM32}h7B`Li#XmCaYAnJXevg`(z`G{UetuCb9Tu!6M z)b8RIy;I9^2TFHRiwt{NjG5VT^9h% zbB#A>#H>!kju2feu$s1oO-&|fpUN*nBe{0}uUlKH*gWm3W6S_I)-3=_=w``!M6=Bv z5OcyseYgu96;*VuDmuCc7(Shi2|Ez971}@SBP=QUS!hzy$BYu49VEvbr&~q3IsRgR z+fA{*)mC7~;tb}@Zj+)@2ll#Va+++WBALCy-QZMr?!jTBF21I@1qw`rrjIEt$Pi61 z!xcdKWjYDY)JahGwmc@7t=qymp?cVi3Ddl|_S~#XinZNKLOx@0rT?~umTe!-DTSO* zXmMn($GxXrG-vgLs2Pe0Mr~@u&a2|_aMP^9MjJJ79T{}9sQAFj-4&R-cZg;kp3!7i z*0JueGvv8PGWhd5S`MmXZ1$1NxRA={*MUu76{|LsuSYVlV9v<}=169ubTDiuCx!ju zB5T)db9c69wEn5#5PhkR;WWZ&&fq@X5bSU~!-2$HLv2&nGXuNFawyC0Va1{jE{2dZ zY}p0pI8<8na50}SCq+d_$Np-?6bD1Gn;+gk)7s#WAJOm#h=(-RI&ef|EM|U)Gu>o? z+SWq$m&@PQgmYnoau4=GbBY?;Xl+(^2F1Z57ztqyve6X@%haA&&%$pG; zjV{8t39x4nGqYND7DPKE`D|B0WJ;Lh4!W>59piBtsK!AXEq2*x>?Jxj)24#m=v=3> z%rk^wp)+3@aqdTHI&)CkLbEHm+(}}O{&4UYl-{*#rYdv9$o47evPLRe?sO|us3O$< zOdO|HT8-JhR25QL`)Ui~c$htVaR{1q{uM5QCp}kQte-@e7E$xR_-UE;$3JdG^S`!h zvg3^lh9#IU&$Gq{jU)&*BsWghUS-pib>4M}%bzula{sMnj%ML`o$U@dt9N8D3mj_7 zS)DOkVle)j<%}Kq?WoRfsY228)7;_*xnu33)=W_|9J9AOb1Mxim!KP=i0qibgiJ%3 zeKA7`X6SI8?!Pl*$sRPLtCDblGb7Tlb!X5fS#1_*A<}g0Df#X=fipE7P6~9mz#`LV zJ!^T1_Aw&>mX@j@iksrLeWXLnX{up%=2t8{LmjiT;sC{!I9>bC3|%W-W@f&bI_?i| zqyIZrijI+FH~Yw&6?d-WBMWPm?6RY?=Ss(Hu(5OJ%D1`cn%v_CaQ^JaRAGP#fd4iZ^%B0yA?bIo(aoX48%rIVe-qa2lMC)NIpBY2*aXLnBa=HcfQ;lhLKaC`hW%jt1h$MEQk z*|Q{cwE0qYDeUMtP84hZEHSMq&)F~Zp!)yZQv%(^jSe^GIF?n@FouzR?&X>Vo`)-| z4$96r9x;UIlfBR!+46j{XU&aZfvbJ0JsiSyM3&VedBsZDU(6d1Q?zBKnwF8LU0Tdw zbd>E}_+~_aXI>+ndFf6_PP}My;n9IiNm&ABg%&p3v9RblSZMoZAMc)sWuJDweFOTp z8jA4H*8DvxuNkqx;q5Xn?p5&}VsYc~i)Yv);$mS^zTgL)c`>hv0lGF`RsH8!Qy_Et3QPYiot34?3_;o z49&_ANq!t0aqr4Ni)1@_0i2W6!~2jq@G)-_gV7RgVvq;#dznK+jO-LV->?lF-f?Al z8H|O`E-&pIUultn0p1k7I0FqnTLP2dB|J)CU_QEXyFd-F_3&}8Vu;}yc+`TyI*B$h zSPw6?VC1;qgIdtU55rRe0&$bkV`sp}ybTQI!LwW&vKYt|^ENWzCmX&ua43sTB+AX8 zRHDI{umB*h83mQWVnb!H1m5@3p&+s*;aNI{74W0IelL;siiZd=1&XO9XGpa}G=qdh zw=kF|(Iy5|TFl#mUvUJN!y^p?>If=iKqZOZ5Q?kEufuvRK(I0(Ys}lsfP%A|u?3hN z!qCLvCW&6ppch_pU}T@*H!yBocPJjAIEvm04;?U|Oy9ft&=AudRsf;6 z?f7K@P*VmJ7W1yhZ{YC0D5(ti&bp}vV@X3`&>avE5tqO(YYhbmj31h+L2Y8dG?0tm zz~v(b6oW|;-OOOJM0eC9FxuV1AdWr^;bRdz5tk@~XC%6X!B63RZ{MMzUm?P4>ztw> zy%oD*HlmB*9gBGrGGSgoZDEk7f*6@<@F&pke=L1}>*FxDC&lEp)S%O^f{%GyAwe7q ze;GWg#a!x4ltDdw9$FQ5R=hQ1h*ipwDm5`+wL&7H@rKA?JG}4RS~66R!Yr2pH8Hpr zUhs^pGI$0}if3e_!872W-4cFAZZ!B^2s%uZ0Y85e7Df&mJcBzG&&Y=jp259}XXLoS zGdQVuM)=ncp$`UMP&^}#89W2ij$5BYl`6#XTYz?L=Gc-DIeuwkPizJJR=2SZcPkkE1nTEs5UW}uXv7zjqp$*t(4aQ zh@96Dp}h*wfk;LSw}}C-Q~|zXh>;_L-@@RiM4K4!Zv#rw`w=;9@C=?+JR{E;JcA!A z{uM-cwXh^c(}~m8=a|ms|1NyY+s@|ym5K5$-7knbHik9YeiMZC!!sR2Q$UVs;>O~a zgNaNP=rr{rYg;Geu$f}sHh$(njUvjq#7rIAs4vr{w>^z0RRx9|WPpPwMsXCwGZ99q z2ScOXusQLL6+m{Ix=hPuqfuH#ag%Q&V%`EkC zc)>IB33z13;H2UiVU2)yDs$?PQ|42Lw9K@SdU#rigr?9Z4{?g&&`SbCe)oWEPae_< zLV!}#rCtncD}#P`!80P?5PMAwUQ;{``Vq9KFfn50ku40U4#BgDI}~8#CV13_!5xZ! z0Fg5W&tS5t{mDaYaMQI>Rz}$Bn9LYZ9g+jB^XGzZV(_}+8Tl8%Z(%?i6@Dm4Ts?lx z+`ExFnE{U_A8T%Akd3<#-Dsk;aE^pvU~mE+nVmkw2)}d! zo@#mBMA-*8t^pg40UOy5SxI1N*@sQ^C<2-auwO-R-b9~8Kn(!P`qQ=%H5D^M1*_+K zd5Ixyd;yfh9Z+aCmrH4`Uxa{7#~qGXG?eD7qTE#BdSC;W1vBBXT;$SVn`m+a15WV3 zGI&6uI~Y7K(G3hhWugyqQKmSv1_t;0cLJPdE179IC6CYN?l6H&Yj!XV7QE z>1Jza@6C?ApAmb1&~Q^-|7fBNX!Dr58JOKuGbeh}3!1}T(9E&3$?%3M+sZN1=xHm1 zd{IL)^~7ESxeScTnyDw#V`PkFuK@#trzF}OcAn<2^E9&$8AWboP%4TH_I1}nT}|+# zu+XhY4lzor9_0-XWRx?;DDP&1M)z39T6C`Wt7q9?)G1zu2p4Jk%Qgl(;L)WS?3QTr zT?lxNfR}m9fcl&N7)p$2zgXUK3f@7nJgJBfk#nd zLyT|)a0ERDv-+}F*cJxoBzgmbZ^8TCdt*b4eAnPLuiYrJ>7_CW^-7(#GRVOD-oe-q zBexj5raBGKnW7#tR0fa0ODaa#?_q43Y7LaY0~DYbY=IY6Mz$NgvT^`Tz(Xqx=E38% z;MO5Vc>3pPi#u4F6NVB7)8I#YHy=KH___BE5uhj{&b1Mo75F!Gq-H+>nwlO~E^ zasBu`0gqxaI0YZa*r1N&qwr`!2B+Xz|L_osm@mL1J%cylvlVm-m_LJOX%UDcOzyGR zN*s?>&*OHS@XPRd-k@hN0K_SGe#o63pIaKFn8-#eQ9(j-lTv8hOXn$$@aymenrzHl zrP&$&o#k*3D1hR;glaNqfX}WbE6(}LttN3ufhzUqm@@}Lc9tWJZYNMYDTZVQ2jH_M z6LUAbE19DbaZU-30=z>|!RqQ00L|b_+X`GRV#tlbcKGbva)9AclQT7QsZ;?!21^9A zR6kZUfW$HVK6r>@U@UPngJKsU&2Gi$+F;Gq(r1G?9l$Z9ZiiQ~FcSO#B#(et) z)x_X_iEd_a9^Mq0v1bf{!4D+JI1@B6xLTr{8Sv=IWX;%3hQOdlqD>6AlR{Pu?t;%QDObAtgdr#? z<(fIKi2()MLs=*H#(g=)~lh1wTrL4t5oBOq7I&t{L#q3OO*i z1wLC`1uz+52$V<0^@3_*V5mU>xo^APP&L2906%C@40v}Y78?wv!JGUTt1$#k_7r%Z zhUY-S@CA5uA_hN_XcGgjhGpJh#E1?`x*Y;{h|nemcS$s$a!K)JLuK$%HkFI7Ul}R` zywAhTwQLE1#e!;LusoZ(6W}gGWpHOUl>^yWc&sG6`i9b?Ap%@pIHTiifW`1*z%%i! zx8B-6V~8M!HHV8M=MF(NcOc*l>~wyPG7pT5%JJ%JOM_AI5imWL&8R4M5LaAw;&Vu7 z=IAB{&%kF}(0O3K1@DxMD4PZ`af-_E#=aFk4Sfl;pTaw7UkCUG{Adh%*m-C7fOMe*yLlcqe}zL_Pu^Xg`Velxtk;~XwLtwzgb#{hK$zu)bo#8|9kivjzVSx;|J|_o5%5V(xI6%W0^vFr5 zKyh{@a#3Cmi?F491m+wdcpmmRP+u2nu#b2S;E%IWRQn`&*F*?Ug?Dt309Y!hEey-B_s3rzY@YyX&Oow4$ zaCjI4F=ino&(2Q_V>kv3x1F-k#>!K(CMTy{R#2y2o-dsO?g7D94Kd_w8~3V*82*{W z7Y{LfNaEp30kneI@U-kzYf5Jf5hzx~3Lpr~gLl-&5z!1m@^tW4fZ6qDD|NyQ;BY7!1gi88RGzkiHX4}i8e8K06sel$~yv&#$sSZZ(_itKQ|fo`px} zXYe?@k7Z8K_!K~M90&HQQn@6WxA+rD3*ix3ySYw{9!UWXr1?$1H^ zM|ejrQNMzBUu+3+pZRMPjk*V-Fkzp`H@OW#0 z5i_c7T#x7`!Q))>!-$+zfDtZgomS^m^kd0qBfBQk#j#OP$6}avx;{)CJnfzgY8^a| zd`|~WK}3T=E((xDtT)Jq10)d}4f4?dNd$9+%(DSfi!s{U&k1qHkQOFDAceS{_}v8$ z#WLuYXfXEg9*)`v)M>+|LY?*HEub71YV#?m=0Q92+?d#v_ty;JrhP+zpS;%HUqbGdGS_!0Vuew?p15Bh?v$XF&0SM>=sV0s9Wr z7b;qjZVx5XV}>zQJ`rCw$WZx2oHNL^VQ=O0l)d@MAy0HtJu9|l;s0ERw@aUxs>fy0_k+1>r zQQr8I55Lr~22u8OlGIU6T;60UHAFfYF98>YiFQI!VWZ6haS^=eVE~#UkTi%_d?E-? zOu(UH0)!TeoLmi{Sm7!=ECDp8NwW$ba-iTLhf@oxZl@6(TBaghV<%IGVGMhPBIDjY z9C*c%soOAyy+V;-T&0X;8ZeAuPf;WoIpB#S(*eU6mQj&l+@y?TI&2ujaZ{0CY*I#s zz3>>~7!1Ihxs$PsAu!+^oIR-mVUI&MEJu`v<9mZSLyV}H4MU7EEruTa2Ce`x>Xcy> zJVr?d6oko06S-QS1ky}+uAr()_sb3rpjjuAKxT%sC0;qiFabZ>tFI}gjS(mo1V()# z2rLqWPiubdw1t4 zoo^>m%{#GI8>>tRjhyH3QGt=u4LT=Tb%y?CzS48R1|V>T{%%~xXwpwcuppqS{F$(4 zL+5$R9mD8HCMx|r=m&<;uP9VH9}8~D4H-=SXQnCrIBG&;RA=ZP#fOe2|5rd~4^U_5 z*UV5l|H%9B@*lxdag)9R^b?vGp-KOj2n_uR(65EA)fqbfs*IuI<8JSm5)n>@k9+^M zROw1G>E4{H_)|!C#%W)Z?yWMVYdV8JR360}dA|wptDtc{54b$CNwGjEHwGbu=*duYR<^jza6RRjocft+BNiS{Eu3qc$4468x*g9`6@eI z*-oWr<9D`=jQ{wqDBk4rV29%0NSIZ9w!C}yDqZm=-S}?B>-E%1H}##_uXM#PLF8)q z8u&UzB0KiLrRqz|(xfoDK9Ayx<#lds@;n+ielWSLKc8&?^8eo&F;Q6qX4^`Ej zLgWGX)9`-=ukJxaUVwiQ{$)iXyb#{yecj=I6Y<{@Mw#PIxzC{755hkL&l5LwO{l{b zczumR@pl2ow*vXz#KZ9F*e=h(vz!Hv{7rzjz{AjG(%gZt7hWAzgF#&WdS#6w5$3}e zz)ypp3BMSA2|Rh~;MGk=ggnLY^WaHqfLF(~v*9m?Uj$FuweXGb>*3XrCjp;?uYf0Q zBfPo<7hGmd^908aT`duCkIb3>($ zyl3Dk^KtmE!k>dzN1B`WIly06B0^0=+<6E8B;rrQbAHcv%17KY@c#n;TX=QEJqQ1L zc-NkYdjbAMc&+oV5dSb_-VJ{*Ja<&;l>Z-rxEP z@YS&$z6Y;*XT4unqK&hS-hh7-p6xQzNxuPc9s#qp{yXkwiNOJYkEI9N%hLzbt*Ha4 zc8@?;Z+rJ(M+#2^(|eNmLy{)aiS%CoKzmPXd*47RX~3S=bfztnYVBytv>8-7)6tRI zZQ}=f+f58R&N!RMpA++c=EeN)d&zSDONWyF#}BP3^ndCl3)gNayu(k&{7Vx4%?bS8 zTDbjW$xVK1%r9EwPe|0xDV|eTG~371waP?U@iqQ+34dYIzdqq#p78S%{y<_5xQgeM zmsf6`wVeXpT{j2@)c`y6RV}4!IU!L&4fj@Ko73BR5FZpr*i-(-jJdKy? zJ`2Q;ykwF8=U{okYwFeum?`-SpO_%R!Z!hca>TMh7E4w0DT~<|5d1bPqs_ej$D_gHz z+}bwKL+apxvOT?nskU8Rox96N!gY7;YA+v54+PDZ)s$@DuHLSUX-_m#S8v8N%|N=n zHPg1Mn@x)PxGjwFy}hRzctmIm63Gm-wUZaPv}s?&I#Rm^EuBrx+0dUF^T#HtW}y-M zD*mup{38C8RozrkQBhf2S6#Q)PbK4Hpi89U-06SYi_Hg5tSTM{_OEELUe7x-TPaqy z-I{8kR9gDsw%obDr>_I`0)i({=rPzSW!eWmpT5JSkauK z;@PD|bBY%gSNOLi{V~rbifhYms7z32vD{2_pj2&d8 zJko46y9T?uGi6=9R21B(ICooqL2T}2_*d>1B>Z&C(xIZ+#dC`4mR1x!Ux=>+Vlh64 zi+k-zFk%A@Q?AY26okr<%&p38JRAdu7m8av3@kB!Lefts{P9UYlkle`YAfC)3Obg| z^~YrVXJhfUnE&8yYy8(eFeT=cSJe86Bf?T6zcRelR`_RbOFle%&p+4s-;b>kLQ8IK z4~j=d(tmSmR0!MkNCxj zwPh8nOZ}(4BX$0xhmvdO)cN^Ie?`)-g9+67Uxnyzd&#<5|FJ{I{2zLWs z^70CdvPu7CPmCnzpG~1zNLDp3msQQ9{p8K&Pow{<+mc(a@sGu7Ru`dr`s3F4A4B?I zq1=gtjMIPPB`elK=#i@0a{uG8npR}Az`rTs|NU(({`Q3b#n>@Oise#%ye79Cnr>Np zjsL?~P5F8fK16&jn&MM}e>qlD;lFlU#{VZ|acPTx@R;Ac24h&czq$o=%J_eR?0*K@ z(4jT{wMl<^jlT!K2~2b~NVf(>O(IM{+xNz5`~wO9K#jk$W=_R5MOXVf68?FxJq{5M z#*&TxO_aRWzYp*~K;#z=)i9%}8UGotrf81;;%#f@l>0X&{kviqWhS=NViHaIzX0bW zhcNVfF!tq|Wpj$E;-g^Oe992_uqz(fws-dp_G0Sp>g&Z|o@pD%4ECF@&CbK42LFjz z%x_Ije|%B#BDB6g;aHq&2kEr3pBu5uxMEWW+Ee|^p|!KEx1&2XUd}GX>!u!^U2&iMOewEe4ssA8Vi_|s#>wadzHEUK$mv2IyanZK{bA6HZ6r)&It1P5yx%C0M_ zE4%K(Gh20c+2ytMWsqPv|G04R(&fmU+4+Mt{`i_QnFjuyw0QT0^o4q-Qv>_D+EeN5 zIj*~-&oqm>Vu2EXZ>_Gr;(%#>LW`>pgivS|JgV|9!=J>+iPhDvv%%D@<=g2CJGZV9 zQ*V;ut$D}AljVddh!~H%ytqnCU9%kC2Wu0}6+bS#lF*W|dq+hTb$eMZ0b1Um^Si64hvZd=PDl6(L z{fRaH<~9ChEfwfhe(vtv#%-lDi|fjltuOOe$>PPIU*lhvS2OpjxtmH$mM&%DrAS=o z_pk8}v_Phxs401uKmPtGhd-bUv%&C)Axq{_BQSiWCM2D7l|O2e%47cJgXyA(_N`Z9m# znk)R-FZ<(?Q{F9W{@j|}rn=g?I!xH8cbSOVQ`5+mT8Y5}>yKSyfTbm6P#ojsg#9fSRgSGfQDZrle`SAFI3FuC3<~ueEY5q$ zl68vtk_;LDRZw7f@K6#QFL*WEr!JjUQgp{u7~}4ul6RG?TWTH55;1?>ZseW)kL?3< ztc4Xat^cG~Q@5=Q=Jp)gxhxoRGlxM7Mv?#L88 z=!?bvb<>Ir(!-{eX0e<-@7QwKpdu_XPz1)|*if{o`8>ogkIlenFsEofM~LF0Vgz%F z86Hd&5iY`>5KW!eSYoy#HMu)WXBL$d&4XGb=u4KD`-OQ&N-8mj6kmq9rnsSQF{T-g zIrFgDC|X!vUR>_y=e1yBUbJKCl2ZQ?jzG2LvrA`{&Y6u&>gLd#qzCpS5yaWe<8vCZ zx0sZ-duqun?*IA|B_%~mi!rN^v7~l=;nGDF$BWk0HO{Uq)9}7&u*tprk^LcGr7$l@ zj5tBK(@fTyRJxn!HSBm14U^!^!vaPo;91b`zj(1Jvr7Dj4rK<*OHj+A5>!BA=u*U$ z-+&tB_*d36t|xEfk-_q%_2lI*wq{txgRiWpR+#0-pP1NNxVmUhin^Fvdqd%=t3`ix z2c~>*-P8vP_ijb{g;Z;~KPE4MCcI$*wk}1*ODoFd%;w){GPc&t$&)V&#J#l_GT{qO z@5WToP;8CxM6w;i%(-yfJ9QzU7i>Ux2$Xiz9X_WqU2NE z88KOE`M0boFY*(4YbMVrnu{GDM!(J)41OgU3?}OmC1^K4w}tJFd+G6e&ANpRQ*J03 zTv~{8Qe~d8#;0MM)S_h+bO+SVT+0!tgX|(g?w4h z|8ep!XfoL|9!)1)U2BDCJyp{Em%n4{;p`ac{cK-8X!SpaUG@FgwBz90pN3PZgnt5O z%dcRPIRWfz-ZB3e!XE+t4Dhdk>)4^h!fOB5IFA0vp&Ep15GMVr6Lr`})vYQnudVAW zs`Jm_^x=+J-X#CGx7FN-vxfu@6>9vOu~5U}S`G~S*KyGEu~>2zBFGJ=60h7=<9{;N zQicc-3IE=+^$vuP?w^U}wNwh`L%{t1mgCP4)hvw9 z!2H%IR>dc6BU;r3TP15%?FoJe8W2YFXDgM`uu^8bU~Fmc8x9>F?8FrXy_}}HMFj7 zXkF2?d38hM%GNDaIQ;LBCp%t$M{7_2VCz6zPwPM`V@6m$5ojgHK&q{ywW~wZH#Ao< zt(DfiY1#UQ|h z6JZLwUt6XO4@PP`+Xgy_0OP*h{rjrRIx4RuV|Ti& zqpPhu)t;%vGn7QxosLyx_@}6?&bq4{mG!qlg;kye)h1Z1BCOuMkscQ9 z>B^K1r26~P`qx_j;;ZcXti+>~9-LwW4rop5e&}In>*o339J=a^HnVPEtJ1 zkgfaLx(8E1BX*^UKr3pic66ot+cHqa0K}&HThrbA<#RsD?oSQ0cD1L|tvxB%B=&U} zLxCu-BV8fvt(}3EwyfOPy!yKJ9v?)tq5@{7?rCf9?CK4RBBdMbM^-7?jICT?HR(P) zucD!7NqbYNj&v*9Y0n-M*4o|1O)FZjUDmt_#i%l+HkqmIVH0f8&eP9wE+g6<5kYii z^U6&MtX{umS>x)4pn_f(jx~)ijQLhkeY={;}eeFH% zPIK*U>rSUUirn9}H`Qt-T3)G*v7kz?tFN^y-Pdgna#=Gx$+9IPl*>QYKqGNqCeym79|HTkdcAZfx?5tfH{G?TH`S3q@4!PU7)|SPT9PeEwgqrN zfE9J5D}%--YEO6HuC{KYB%H`$$Kj|Tj8=xxDkKl~f_DNqQCd zsQ1pa$&1y=^zGTxozmh`$!?cq@x`1_9gcBE4WY>g8e=02_Ol#m9A;bll&0TjEv$Vo zo#}JepIXKh*KJI8^}73d_ax+p@_XBQQgYs^ZwAOK0`>_q#vj-Y9s{>_q9LUeRxk?@*b5$S4>)22 zJms@c)m1DNx-$&Mh@<>rRoAb(4%48tnTcPw>DpzjE7z^Q*3zz9(R6*2jW;g7 z_Iiz0x2|5_ylJBaodhcy*EX!)QeCyylHSv_`nn3Exc2T;8^%)T&E(ZsZ6oWNHrv?h zsudMBdfke3D>g1$r@V_-H3!kg=3&)gC%5#K>l-wNdbFa<92Evkjk#@g+8Hlxs1{bQ zT)FZJoNZ;Bf@l#urqMX1w+{{sqe;A;}A7FCQ%r8t!QZmoo-EDi)UMuFX*6uD;+RTLAseP&L zK&n1J7HrzvXZw_4?dmvS6R|obVQXt!zqdCne7)XaKPElT%pa{B5m^)rIw*FguU`qh zeM0I*m4b|e2gYD-_d51>b!0j{)CGxB2ddiA?Ok1_uOowYXpw`&{w}D;>%|I(6YyY9 z&%tm4gkrbScj1#RrZ%BOF@b^{?JQTtwxgWW*Jgg`e&mtfrx**v27p76n- zm)?gCZrKOZNSw{z+tU@0*`e08HdZ&cZfaiPagrNI**PL0c_wB$vW+*cZLM0oZq+tR zlP+o#upe8lre=*cu4q_cv5o9(hJl^Q#I9evxyeQwnwx`ym}$$fupgV)>J?xyQ7liy zJRCiv5>RY=_uh^kSxuW7E=IRDxVH98*S?fZk5Ph^wxg~rSS>}yCTPaPkb=oTMpNh} znj24Z+hJ&XbX`Gq^1*H}G%v0WRBaK9BZ%tC2x9RS5kz%VM)fcm!;)MX$k>zWO=0P6 z);?+#rYu)f1Y8yoEz1>E5st<0jv%gx7OpbPdRPY-{$$oFWl12Fn&hh260Qme77V($?gN$V;P(7?Ao32x@D^qfZU--T3|5I^x~8= zooUAu)QV+~Sqn$=b+UrSZ1u4$6tz;Gn;o_#SZB3@j9CyXWO<3gkzm$d!*K-kK;x-vq$Z3#pI%)DvkMyIs|OF8a|LLe-#PGJlBLYT-o2nZ61 z4cgw5Ra`YmwTpdNqGC_U6_+-MN#@ksAk6k^MAh@?BdZHZAHed2n<(govrqq?zP=9b zRjHA|G_4YLn~|`;uQx(zXliW4sD?4i#<5&5ko{Z?k3LK8t zWH;-egbmVVH>!A?wWQmy5Wyh6yKB!Nj-L#hY_%}zFKG>qEHnwL7fxVy_iHxNs4VwC zSt88vW0pP0%;rxPN*A~d8`W@a32Zm^&Ny(=9A(Lr7O`F?)zi;}VBwlcS)%imT}+v5 zZAx=+ViWUBLlZ-s6gjFi_D@)}7ARD3s4Z@1&0$(Q-o6vw@Yu+aucC3fpjcZgNsVKJ zaBK?oD|0$e0I@V6eGuDtEv&1c5$0h%Nx<>kusK?lqoOXVNP8mT>B$5JAqZ?mC+OoxJT?hNy(qclQ+>0sP(F#uxFjsnZF+4msisoy97{2i4J>g9gtZcz|W0eVKk$jyzz{gOK1nPOz$q@R+UxW2AW($w<|7 zeFiB@GFy)HV4C|aa}+2CV*A_hxrQ9&ba*U)YTO=d@Dzn7U!7fhI$_Vj8B40Ma~Y;zQ!x8H$IdRn!cdk!(e5aXK+dZUuq{*v zD10Sg4(L4gY|miZjx*^B#Sy4hAX*>j6RisoRm!0n#()ML!0xeYridU9%Mj4e?shEB zE>u`ndos~Xd2c)(oJ=i<#Wf@a2}AMohz}9u6-T_a5hq9hcm!|3~YJ#KQ(YA@An&a}^eXMSEpq^hveOFql(qHJC^k>)IqBP=djTF2yNAuk3NDXT3PI|7>KA`a(+b3u^7U$ zq;%@$!Bj7fk+US=u;rkKQ6tsSDoqW23d3%s8&+AQgqi|-3Q8tN?xw))-JK@?bUSQH zaOe`k*+0;?hkFW|q9Aax)Y=BFs5ZC+v05-YKRmPuMzJWf3=Sxj<o1>RRRDXHR@Ma3nR}Ry=7P8(P$gLyYga-5FNF`Jc3+T z?d6Ugy>3i=Y|f;d$+0Q1OZ{oF>9HB{nX%dN;uv1L_Hf7GTQM(wYd-!`I$=6~1UDI> z!hBmCAE^Xz1>gb)u8+XHU&&3+ZwpzhQh2&!fX^dbnO|@;=L7y7@ejuKj{WwxzIi_1 zTZXsoy=i2E+k_{+Ot&IG=V)vdsBl`Q!=Sl&9FM^KX4s|kZo8RIdVWx&u&V>6$wB$} zbyzpz%kuM%#_>_8;`#m8Sc88*@hnd?eFM_-(=3;dbt3)%@Qgz))318!8~Fm0b__JU zm8i7$kj8vTI|Evap-Fj|FY#;`6m?iRNdGSAIS!q0UIZQ=f%!$(h2+fyfuCkgviT8S z09f_x>Vdj#1f3sT)gZnsf7<(VkB<64&K>>-sW<9?^aG&d^*qoE-k)>S{{U&4_Bd$C z5zlx`luI?f#MY`(w-Vryb4xz^J=(mggrL#^z63{uFF%{IYL+{cE(e z?LeIW=2Jj??d#CA(IMqj;NvIe5zS{2a0#>nKaS#GzgV7MdUVVO^6nV@!Q4AXeJJO{ z{zp*ymHD0VTgT>?E*~e~VUs@tqn<70Dma{DO13P2^4+7B2S~-;BV_*p;8-z;5|z z8=KHB{Je!@hP8VsH}TVe$NvB>bzoNxc}{|#-?l8U{DfN~@Zku22C$A*K6-SFEg+?9T~HYXPkx zl($jjEg!eaO@DHP^eX$|i_p$rg!bG;Xs=#`#>)=a@7=m+StehEw%{VPnv2l*r`|4< zU&lpg2QEUp>msyM7oj~m0*y8(_5vE;AhUY8ZY&#_cJU2k7YtxJS#t#8&e5<-e7K@x zC*jfvyaX_Hto%(8xD)UaOLuLV{7*n0KaA1zgwFzQ0gd)exBwl4_PsoRJV)L;VjE-* z4(RouZ+Gck+4Pg3a|~+G^fwy1=KnJ2%u8YZeM`4I(*QGn(%tft=gvIH2A%msx9oF! z*yTzs?KEg_3N4s7;_YKGzKi-XPFucHFrt$Yz7>Wo;HkmQMkgXX^_EA=@(g&6X7fm0 zSk4wSBKfQh$ef-4OjdCr4}W2x5d3BZq=G-iEtcPzeY>{FuL6&Q!9 zFQz*>#iW~Q;X48MBi>;2waboCK{?+5ohwDF&+DmAtzQKe9?6il$))WCjqPO9Wzmj- zhGy2fH|TnX!`FWT0GGgv6`a}1V6;nyro9O-$N8e3X!)=jcDQd%i!jo@2p z>b;p|S2_KlL7f47Gy(_e z#MSP!+z)|g6?m-O>`>h?{p+B0gtVJnS_>9TR|_q${h++ZLCXP6%YOne%dhYm2X^~h z0z91K`K<)=Z3x%q+sxA3=!HBpaT8ZJct%>&k$xxW2ZWwoKGLXM>XM#tDd4*hck;W@ z-3zeX$+1d(?rW`k9IOk#}d^ z34XMz?p+SK`XSE(EcT8I&tzxCYsyOcvIBHdINOcOEluTdvkt${4QB{?~vd3 zm^C=_a%Dk;_cCPh_2^p>ckC{s?Z`CbSydA1hVV(iv|FX01H8b2Ujdu|%=RIF9+(-o zHW}b&UI1Tl0enLQ?0ZM$~yu-Z~^Hmgy*8jOe3kYdtgL%0AYr4$s$OSbp*^zc=oGU!H!X|{Xk>hN#-X$ChR)DmiqQE-28=84FkA;t^WzziaWSt5k?DYG%bMQ; zhfdKvBQh=~V;lcc4qHO`d0&;%$zSilEN`O&lg=|F<4pN^kCnotcQ`Q9-|4`l^Nh(j zlRm)(mbjR-7|)gzX8L{yX8OYpO!_egCY|R^#u@pi9hh{UKe=?CD=17l&!ZHk{HGn5 zbe>NcXXL*cfib#RdR*E+&)Pq=z7iC|9H(M{DW7Wq;|j9=h_|*k&%2B>`LzW0Vf3}r zfhkY>AA2O>7K~BGne>wb{TIkJ|by^>unHgvD5AQuS^{ESnWkWw447-MYrvtM*{bD4P zXy_RS7W;I{|7CEU2Rv7}jr>;~I{7OC`z!Fi16ZAwpXX`Dne@*%{6AI_LgnXqmvM%_ zgUS+Dkab6<^7CBFIKzLd12gNx4$ShNc3{$Z&SjiQ|EL3#&T}u9em(*}Nw!Iygbe`?F^fw)t zbe{DXXVOo0VA6T^iIQ$Ejol>bk`|H}w}4$fVaPJW&TDa`UF z9GG;T2^nX~yWN3F=edweKkUGy^PI?~A9rBVd2Zy=&p0sYJV$cr=Ny=Ho-4WZXB?RH z=Ny>z^Kc$yoM}&GP9a!4afl252l5s{qO*r>am~@^oDa`bz9hh{UBN=DX zKN^Agx7Ccx@y?<>3A^Lt(O?^e`8Hxd%#f_7af@NmmHY&ecgd2f1EQJXXGz% zVA6S}<k-4oo`F$z1wOoMS0WI?v4%ru@YYOghiej5G3AIWXxw zS99q{9hh{Uv$^!ABk-G!{WAX!N1s23z6fi7_$}a02hPD-x*M=rubcJtqG0`m^+d4N z4(G4&^TYB?AA#QDtoIj2=&TRV0gW^I7@r7X(ib@}>(k`Gq;GQIs~~?fV5&gJ`x6eG z{3jil@*Z_y($6|D=}$W_>CZSY=`T7k=`T4j>9062>90C4>90rN!%lmxMSTcsdu;@K z)PY+8-|4`V_omZcy`Zbp^~B-@iivr*k3c`|(8IHau)U`(4AXx$lAiT@BN5VBo-@S} zI5>tX@ID_&PyS<${P66;O*+qFUHUW!CY@)pE`5;$lg_hQmtOC{r1Ol{rEiMB z{L?wc<#^9PUxagj;CTAq03UN;p7k7eV6ji9y>R3bIJKt}PJ0!CUJKzh;F^RSE(6T@ zlyW8lz7p_vu)g4uo%CgZzlp&H*$T`*+&pt4eJx<>SIf5zF!u`z4+5T5YR*@f|3?9T+CFzCJOsE1<16(~n0swke_sLo zsBQ0wu!nB~{zbw(XCVLcfWL_Ggj)o{KLy>T`qsZ8^g!P#K*p)vU@I(~0!ty5p z{}Rel_$t6#A-}=W`L{n(us6+bE8yQG%(EBf-wrs2_SE{^40s{r9k=NZ0XEvEj^f@& z06*`P|0LkgVZBEEQQm(9JP~zL`}q#w521bsEd6f)yY=}e!1tidxn*GboLtZQs8il4 zfL;E%fS+67dG+9D`&UQkD**r6+B4;C0(>*fJzv<{d3E0hVDd0-jy0*uv2z@JH_IYjZUcj5tSc`1_?*lx_ zX}>!G_dEID2l$tcem@J?&HpT5&Vj1$?*bm<$a@~}ho(TL&F>eH^uHs2X_$U0I{G~@ z4y{iT@Co!UR1x~g@vZ^Duoui*;0dGa)Uv1O>oatdt*IM|$1MWe4 za0^BI7&Rt4|0#gK0)1+J?*jajL};H^Mfg_%?m~T(e+%HnmqR!l%eNEo2ON4o;0=y_ z-wybB^cTJt!Sb8{TmmR ze4hmPtTTRIM#aGC_`d*fE&35lNf z5AZ~kU+wp!5&Hdr{{ro=_V^{h8^Evq={(?4)TajJVgLOe;M>8!z?SbPOrHqb=T*RN zd4Csy^I=O|Kf+8zUMb+MD4gbZ72wY~JlSY3w;}Qy zj7L{m_+G#V9sB$Y;4|~W^8O{@HjGCs57U1iaIM47vvaOdbiVxuz>hoj{42np!u$gj zNcy(`w>kYk2W>MK{jbr|@uzdVQQ*<~%>o>A@=F391G?JdQo#J%JKEmY0sb81w^(^8 zz#WeL_W^eGc^L4|P`-razX$Lqo$;1u|0_{=mH(drp9CF6kn#B2fXk3r?fnJ7zd@U8 z`DuG@e!m0!Mf6|QXCCVIKBUKg(a#jXUV_%oI_7#sfFE+oe>vb!!>hipLHtvRuz$S= zu$x~?1nvU-dCcdlAfN4dGhm(%Yk#{Pa3w0I^|=S|rycwJ4B!~bukHB=;J-y<044J9 z3L~B$hw~L)A@zO@WprEm^MJX3)cpPla0lRe(AhtJ2lyeRhe?Tk$0F}Mz;p%JJj@3C z0obqVw+iqCFe|(Y@Xctfjo@efb^yK)L~XwTz;odhem~;p0{J=KU4Vb#wBP-JyWp2t zd7ni*EKiR22;fhlO|?C~0r+|3r}}sfZ~?|g=EwT+S%=#`G3ag?>{so193mVuv_6*t zUXAtYcANh~z&}WY{rO73pT_uer=?#Dc%@UG>jAs@bprk*#s>tVpW6_r#d_|9g^vNg z)v>ov0=^IYY!B-53xFR&do2RY`g|SmC!O`?i-5VlY!JFH&ssi>@k7U(w?Nzm#tqV7I<|Bk-+&!}YK4-3yp~ zPTTJ@fIpA=X#0I0@CTgweG9NF|5?B_&mO&vHwMY-Ta(CBT}02Zb=RF;nQ1Ok5no*3-W8c;WzpLCEvj6C%E45 zi`1MyUuFNgliopvx1r3RIG2jPM(O z`^}p3{eJeh%I-J1BYuFK#JzZ#WQF;|UcV8og{em?68%^^eXZDwT`f+F0m!9@A084X(=%gBYpVn=B3%&o6hX#z9Xdyngt(Kk9fm@`;qs^cc++Zg~AJooa^1} zC$s!)RO|~`M6OH7?tc1Rvfg~7x9mAL4%mxg%ykEHy9RHVu{WyO^r1mm1l4T}+ujc8 zwdxy_Y{K1zAkHwATkWu;|SMhI7~jS&PH=l+SzR<9|^I zuH2OmviX6ybGL@kqj?F{$aL!w~vVUmUS zhH-wu-&#s%y6}CoGI6NWTPy4}PSLOVotsc?NkT;!M0HsA;HFbUsSNM_h-zA{s)X*{ zTTe|(noBh8Kq;>y^fm1F1(3X<_Gr@5hEp3?67{QpKYV?l34FF zQT3tYgg0W?GKHOW*!89(-D04(00$Qs2Nz#h6^1S7P7R_Y%MtcZ+kNCkZ}&Drl;3<_ zk9&Bv?6&gZJ#t{Om#K6Dmc+T)*=oVD)96h_98~3k-K<+EgWESTioyyo0r939 zBQ0zXor!ej;YkDAS~M4}vN!SC7h9HLRU2tF6VB?qwI)<9?%A*-f3W7Y1q)RugSUGrsF*$!*uGb< z<>Bp9!DTJ92rLimEgCw`xHkuxESw7E6!!6%S0}`vI0V@#BjgK+k@;WwqrB11-YtG{sEurp$Kv4(R4 z_6TidbC-pedq=mia#>;MT4jVASxxR?b)379i~xIQsahM&L9d0k7Ij5vlQvH-_bst1 zdSPf~*p>AfGHX%6+A?|@wL5yrGB+?Id#h5gCIeM&`ZMbXxkX=un_H8t)kM3pne?UT zxKh}?JY5VIGTqOF)Wq2CI**uHmQ$O1iLx_BI9FY%d9Y#h3i7N$4|nC6`+xa8((o&H z$ty#+o#@DA8!%86&6PIpeZ8no zW^Bpsi*QuI8>}_Cd;gx@PnnzVg8h^ZZgLTiQ9)HW8(5u$9mqCn#O4g!;;@D6fP)LX zY;5?t+U6D5s2ab!6}LD$OPX;3lC{Nsc{3;*M-_FP4az<#$a=ys;Rq&KT zc|2#$!{5+8i?}+K$MZGiljk1=I5Bnd)#$Y@AHSoU2tJ&_s#Cu8Kxmva=370WTp?{+ zgzuV6+?!uIoUb#&x9ddQ`#KLzt<${HfHh81={Muv(?*%Xsk9>zzSr^5%3(YgRHuB$ zB7B*r;$8{PZ`CQ^Ny}GZ34iiR-1`|1J*^`j{by~ce1}K*UYX@#pnMM_j$-#K>GR-Y zTdLE1&jF!v(q0B%_&q>qS2#AcHbpKxbD83M|HVY*S7k8dS-HCIslL~b_TVe!n?a<# W6u{##Y?iziulBuKoUN);dH(|rf{7&n literal 0 HcmV?d00001 diff --git a/firmware/am43x-evm-scale-data.bin b/firmware/am43x-evm-scale-data.bin new file mode 100644 index 0000000000000000000000000000000000000000..2d71341089816be7989e6dc11b265d9194ac909e GIT binary patch literal 41 hcmd-HXAn+dU{VptW>OLB0@CSBDpG9>aG{wnApnMi2I2q! literal 0 HcmV?d00001 From 33ea271b41ffa3ec9971ca26ae72430b63e98412 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Fri, 3 Feb 2023 19:24:12 -0600 Subject: [PATCH 15/65] Add BeagleBoard.org Device Tree Changes https://git.beagleboard.org/beagleboard/BeagleBoard-DeviceTrees/-/tree/v5.10.x-ti-unified https://git.beagleboard.org/beagleboard/BeagleBoard-DeviceTrees/-/commit/fab7ca900247d3e9041cf526ea2c1fe284309987 Signed-off-by: Robert Nelson --- arch/arm/boot/dts/Makefile | 13 + arch/arm/boot/dts/am335x-bbb-bone-buses.dtsi | 179 ++ .../arm/boot/dts/am335x-bone-common-univ.dtsi | 2289 +++++++++++++++++ arch/arm/boot/dts/am335x-bone-common.dtsi | 46 +- arch/arm/boot/dts/am335x-bone-jtag.dtsi | 15 + arch/arm/boot/dts/am335x-bone-uboot-univ.dts | 29 + arch/arm/boot/dts/am335x-bone.dts | 5 + .../arm/boot/dts/am335x-boneblack-common.dtsi | 135 - arch/arm/boot/dts/am335x-boneblack-hdmi.dtsi | 141 + .../boot/dts/am335x-boneblack-uboot-univ.dts | 185 ++ arch/arm/boot/dts/am335x-boneblack-uboot.dts | 193 ++ .../boot/dts/am335x-boneblack-wireless.dts | 10 +- arch/arm/boot/dts/am335x-boneblack.dts | 138 +- arch/arm/boot/dts/am335x-boneblue.dts | 149 +- .../arm/boot/dts/am335x-bonegreen-common.dtsi | 1 + .../arm/boot/dts/am335x-bonegreen-gateway.dts | 260 ++ ...am335x-bonegreen-wireless-common-univ.dtsi | 2197 ++++++++++++++++ .../am335x-bonegreen-wireless-uboot-univ.dts | 56 + .../boot/dts/am335x-bonegreen-wireless.dts | 74 +- arch/arm/boot/dts/am335x-bonegreen.dts | 5 + arch/arm/boot/dts/am335x-osd3358-sm-red.dts | 195 +- arch/arm/boot/dts/am335x-osd335x-common.dtsi | 1 + arch/arm/boot/dts/am335x-pocketbeagle.dts | 1891 ++++++++++++-- .../boot/dts/am335x-sancloud-bbe-common.dtsi | 67 + ...-sancloud-bbe-extended-wifi-uboot-univ.dts | 119 + ...m335x-sancloud-bbe-extended-wifi-uboot.dts | 103 + .../dts/am335x-sancloud-bbe-extended-wifi.dts | 94 + .../am335x-sancloud-bbe-lite-uboot-univ.dts | 76 + .../dts/am335x-sancloud-bbe-lite-uboot.dts | 66 + .../arm/boot/dts/am335x-sancloud-bbe-lite.dts | 56 + .../dts/am335x-sancloud-bbe-uboot-univ.dts | 67 + .../boot/dts/am335x-sancloud-bbe-uboot.dts | 66 + arch/arm/boot/dts/am335x-sancloud-bbe.dts | 72 +- arch/arm/boot/dts/am33xx-l4.dtsi | 32 +- arch/arm/boot/dts/am33xx.dtsi | 4 + arch/arm/boot/dts/am5729-beagleboneai.dts | 121 +- .../arm/boot/dts/am572x-bone-common-univ.dtsi | 2236 ++++++++++++++++ .../boot/dts/am57xx-beagle-x15-common.dtsi | 8 + arch/arm/boot/dts/am57xx-beagle-x15-revb1.dts | 5 + arch/arm/boot/dts/am57xx-beagle-x15-revc.dts | 5 + arch/arm/boot/dts/am57xx-beagle-x15.dts | 5 + arch/arm/boot/dts/bbai-bone-buses.dtsi | 646 +++++ arch/arm/boot/dts/bbb-bone-buses.dtsi | 315 +++ arch/arm/boot/dts/dra7.dtsi | 2 +- arch/arm/boot/dts/omap5.dtsi | 6 + .../boot/dts/overlays/AM335X-PRU-UIO-00A0.dts | 28 + .../boot/dts/overlays/AM57XX-PRU-UIO-00A0.dts | 55 + arch/arm/boot/dts/overlays/BB-ADC-00A0.dts | 112 + .../boot/dts/overlays/BB-BBBW-WL1835-00A0.dts | 120 + .../boot/dts/overlays/BB-BBGG-WL1835-00A0.dts | 246 ++ .../boot/dts/overlays/BB-BBGW-WL1835-00A0.dts | 214 ++ .../dts/overlays/BB-BONE-4D4C-01-00A1.dts | 224 ++ .../dts/overlays/BB-BONE-4D5R-01-00A1.dts | 224 ++ .../dts/overlays/BB-BONE-LCD4-01-00A1.dts | 276 ++ .../boot/dts/overlays/BB-BONE-NH7C-01-A0.dts | 232 ++ .../dts/overlays/BB-BONE-eMMC1-01-00A0.dts | 61 + .../dts/overlays/BB-CAPE-DISP-CT4-00A0.dts | 210 ++ .../dts/overlays/BB-HDMI-TDA998x-00A0.dts | 188 ++ .../dts/overlays/BB-I2C1-MCP7940X-00A0.dts | 81 + .../boot/dts/overlays/BB-I2C1-RTC-DS3231.dts | 69 + .../boot/dts/overlays/BB-I2C1-RTC-PCF8563.dts | 61 + arch/arm/boot/dts/overlays/BB-I2C2-BME680.dts | 31 + .../arm/boot/dts/overlays/BB-I2C2-MPU6050.dts | 53 + .../overlays/BB-LCD-ADAFRUIT-24-SPI1-00A0.dts | 180 ++ .../dts/overlays/BB-NHDMI-TDA998x-00A0.dts | 125 + .../arm/boot/dts/overlays/BB-SPIDEV0-00A0.dts | 81 + .../arm/boot/dts/overlays/BB-SPIDEV1-00A0.dts | 81 + arch/arm/boot/dts/overlays/BB-UART1-00A0.dts | 45 + arch/arm/boot/dts/overlays/BB-UART2-00A0.dts | 45 + arch/arm/boot/dts/overlays/BB-UART4-00A0.dts | 43 + .../boot/dts/overlays/BB-W1-P9.12-00A0.dts | 46 + .../boot/dts/overlays/BBORG_COMMS-00A2.dts | 63 + arch/arm/boot/dts/overlays/BBORG_FAN-A000.dts | 27 + .../boot/dts/overlays/BBORG_RELAY-00A2.dts | 49 + arch/arm/boot/dts/overlays/BONE-ADC.dts | 28 + arch/arm/boot/dts/overlays/LED_P8_03.dts | 17 + arch/arm/boot/dts/overlays/LED_P8_04.dts | 17 + arch/arm/boot/dts/overlays/M-BB-BBG-00A0.dts | 21 + arch/arm/boot/dts/overlays/M-BB-BBGG-00A0.dts | 24 + arch/arm/boot/dts/overlays/Makefile | 46 + .../boot/dts/overlays/PB-HACKADAY-2021.dts | 107 + arch/arm/boot/dts/overlays/PB-MIKROBUS-0.dts | 107 + arch/arm/boot/dts/overlays/PB-MIKROBUS-1.dts | 108 + .../dt-bindings/board/am335x-bbw-bbb-base.h | 103 + include/dt-bindings/board/am335x-bone-pins.h | 253 ++ include/dt-bindings/board/am572x-bone-pins.h | 179 ++ .../dt-bindings/board/k3-j721e-bone-pins.h | 242 ++ include/dt-bindings/clock/dra7.h | 4 + include/dt-bindings/clock/omap5.h | 2 + include/dt-bindings/pinctrl/omap.h | 4 +- 90 files changed, 16185 insertions(+), 725 deletions(-) create mode 100644 arch/arm/boot/dts/am335x-bbb-bone-buses.dtsi create mode 100644 arch/arm/boot/dts/am335x-bone-common-univ.dtsi create mode 100644 arch/arm/boot/dts/am335x-bone-jtag.dtsi create mode 100644 arch/arm/boot/dts/am335x-bone-uboot-univ.dts create mode 100644 arch/arm/boot/dts/am335x-boneblack-hdmi.dtsi create mode 100644 arch/arm/boot/dts/am335x-boneblack-uboot-univ.dts create mode 100644 arch/arm/boot/dts/am335x-boneblack-uboot.dts create mode 100644 arch/arm/boot/dts/am335x-bonegreen-gateway.dts create mode 100644 arch/arm/boot/dts/am335x-bonegreen-wireless-common-univ.dtsi create mode 100644 arch/arm/boot/dts/am335x-bonegreen-wireless-uboot-univ.dts create mode 100644 arch/arm/boot/dts/am335x-sancloud-bbe-common.dtsi create mode 100644 arch/arm/boot/dts/am335x-sancloud-bbe-extended-wifi-uboot-univ.dts create mode 100644 arch/arm/boot/dts/am335x-sancloud-bbe-extended-wifi-uboot.dts create mode 100644 arch/arm/boot/dts/am335x-sancloud-bbe-extended-wifi.dts create mode 100644 arch/arm/boot/dts/am335x-sancloud-bbe-lite-uboot-univ.dts create mode 100644 arch/arm/boot/dts/am335x-sancloud-bbe-lite-uboot.dts create mode 100644 arch/arm/boot/dts/am335x-sancloud-bbe-lite.dts create mode 100644 arch/arm/boot/dts/am335x-sancloud-bbe-uboot-univ.dts create mode 100644 arch/arm/boot/dts/am335x-sancloud-bbe-uboot.dts create mode 100644 arch/arm/boot/dts/am572x-bone-common-univ.dtsi create mode 100644 arch/arm/boot/dts/bbai-bone-buses.dtsi create mode 100644 arch/arm/boot/dts/bbb-bone-buses.dtsi create mode 100644 arch/arm/boot/dts/overlays/AM335X-PRU-UIO-00A0.dts create mode 100644 arch/arm/boot/dts/overlays/AM57XX-PRU-UIO-00A0.dts create mode 100644 arch/arm/boot/dts/overlays/BB-ADC-00A0.dts create mode 100644 arch/arm/boot/dts/overlays/BB-BBBW-WL1835-00A0.dts create mode 100644 arch/arm/boot/dts/overlays/BB-BBGG-WL1835-00A0.dts create mode 100644 arch/arm/boot/dts/overlays/BB-BBGW-WL1835-00A0.dts create mode 100644 arch/arm/boot/dts/overlays/BB-BONE-4D4C-01-00A1.dts create mode 100644 arch/arm/boot/dts/overlays/BB-BONE-4D5R-01-00A1.dts create mode 100644 arch/arm/boot/dts/overlays/BB-BONE-LCD4-01-00A1.dts create mode 100644 arch/arm/boot/dts/overlays/BB-BONE-NH7C-01-A0.dts create mode 100644 arch/arm/boot/dts/overlays/BB-BONE-eMMC1-01-00A0.dts create mode 100644 arch/arm/boot/dts/overlays/BB-CAPE-DISP-CT4-00A0.dts create mode 100644 arch/arm/boot/dts/overlays/BB-HDMI-TDA998x-00A0.dts create mode 100644 arch/arm/boot/dts/overlays/BB-I2C1-MCP7940X-00A0.dts create mode 100644 arch/arm/boot/dts/overlays/BB-I2C1-RTC-DS3231.dts create mode 100644 arch/arm/boot/dts/overlays/BB-I2C1-RTC-PCF8563.dts create mode 100644 arch/arm/boot/dts/overlays/BB-I2C2-BME680.dts create mode 100644 arch/arm/boot/dts/overlays/BB-I2C2-MPU6050.dts create mode 100644 arch/arm/boot/dts/overlays/BB-LCD-ADAFRUIT-24-SPI1-00A0.dts create mode 100644 arch/arm/boot/dts/overlays/BB-NHDMI-TDA998x-00A0.dts create mode 100644 arch/arm/boot/dts/overlays/BB-SPIDEV0-00A0.dts create mode 100644 arch/arm/boot/dts/overlays/BB-SPIDEV1-00A0.dts create mode 100644 arch/arm/boot/dts/overlays/BB-UART1-00A0.dts create mode 100644 arch/arm/boot/dts/overlays/BB-UART2-00A0.dts create mode 100644 arch/arm/boot/dts/overlays/BB-UART4-00A0.dts create mode 100644 arch/arm/boot/dts/overlays/BB-W1-P9.12-00A0.dts create mode 100644 arch/arm/boot/dts/overlays/BBORG_COMMS-00A2.dts create mode 100644 arch/arm/boot/dts/overlays/BBORG_FAN-A000.dts create mode 100644 arch/arm/boot/dts/overlays/BBORG_RELAY-00A2.dts create mode 100644 arch/arm/boot/dts/overlays/BONE-ADC.dts create mode 100644 arch/arm/boot/dts/overlays/LED_P8_03.dts create mode 100644 arch/arm/boot/dts/overlays/LED_P8_04.dts create mode 100644 arch/arm/boot/dts/overlays/M-BB-BBG-00A0.dts create mode 100644 arch/arm/boot/dts/overlays/M-BB-BBGG-00A0.dts create mode 100644 arch/arm/boot/dts/overlays/Makefile create mode 100644 arch/arm/boot/dts/overlays/PB-HACKADAY-2021.dts create mode 100644 arch/arm/boot/dts/overlays/PB-MIKROBUS-0.dts create mode 100644 arch/arm/boot/dts/overlays/PB-MIKROBUS-1.dts create mode 100644 include/dt-bindings/board/am335x-bbw-bbb-base.h create mode 100644 include/dt-bindings/board/am335x-bone-pins.h create mode 100644 include/dt-bindings/board/am572x-bone-pins.h create mode 100644 include/dt-bindings/board/k3-j721e-bone-pins.h diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index f19455a84563f..bf9a58291b588 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -796,6 +796,19 @@ dtb-$(CONFIG_SOC_AM33XX) += \ am335x-base0033.dtb \ am335x-bone.dtb \ am335x-boneblack.dtb \ + am335x-sancloud-bbe-extended-wifi-uboot-univ.dtb \ + am335x-sancloud-bbe-lite-uboot-univ.dtb \ + am335x-sancloud-bbe-uboot-univ.dtb \ + am335x-bonegreen-wireless-uboot-univ.dtb \ + am335x-boneblack-uboot-univ.dtb \ + am335x-bone-uboot-univ.dtb \ + am335x-sancloud-bbe-extended-wifi-uboot.dtb \ + am335x-sancloud-bbe-lite-uboot.dtb \ + am335x-sancloud-bbe-uboot.dtb \ + am335x-boneblack-uboot.dtb \ + am335x-sancloud-bbe-extended-wifi.dtb \ + am335x-sancloud-bbe-lite.dtb \ + am335x-bonegreen-gateway.dtb \ am335x-boneblack-wireless.dtb \ am335x-boneblack-pruswuart.dtb \ am335x-boneblue.dtb \ diff --git a/arch/arm/boot/dts/am335x-bbb-bone-buses.dtsi b/arch/arm/boot/dts/am335x-bbb-bone-buses.dtsi new file mode 100644 index 0000000000000..c70e3c1674bca --- /dev/null +++ b/arch/arm/boot/dts/am335x-bbb-bone-buses.dtsi @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020 Deepak Khatri + * Copyright (C) 2021 Robert Nelson + * See Cape Interface Spec page for more info on Bone Buses + * https://elinux.org/Beagleboard:BeagleBone_cape_interface_spec + */ + +#include +#include +#include + +/********/ +/* LEDs */ +/********/ +&{/} { + leds { + pinctrl-names = "default"; + compatible = "gpio-leds"; + + /* macro: BONE_LED() */ + #define BONE_LED(PX_YY)\ + bone_led_##PX_YY##: led_##PX_YY {\ + status = "disabled";\ + linux,default-trigger = "default-off";\ + gpios = ;\ + }; + + BONE_LED(P8_03) + BONE_LED(P8_04) + BONE_LED(P8_05) + BONE_LED(P8_06) + BONE_LED(P8_07) + BONE_LED(P8_08) + BONE_LED(P8_09) + BONE_LED(P8_10) + BONE_LED(P8_11) + BONE_LED(P8_12) + BONE_LED(P8_13) + BONE_LED(P8_14) + BONE_LED(P8_15) + BONE_LED(P8_16) + BONE_LED(P8_17) + BONE_LED(P8_18) + BONE_LED(P8_19) + BONE_LED(P8_20) + BONE_LED(P8_21) + BONE_LED(P8_22) + BONE_LED(P8_23) + BONE_LED(P8_24) + BONE_LED(P8_25) + BONE_LED(P8_26) + BONE_LED(P8_27) + BONE_LED(P8_28) + BONE_LED(P8_29) + BONE_LED(P8_30) + BONE_LED(P8_31) + BONE_LED(P8_32) + BONE_LED(P8_33) + BONE_LED(P8_34) + BONE_LED(P8_35) + BONE_LED(P8_36) + BONE_LED(P8_37) + BONE_LED(P8_38) + BONE_LED(P8_39) + BONE_LED(P8_40) + BONE_LED(P8_41) + BONE_LED(P8_42) + BONE_LED(P8_43) + BONE_LED(P8_44) + BONE_LED(P8_45) + BONE_LED(P8_46) + + /*P9 header Bone LEDs*/ + BONE_LED(P9_11) + BONE_LED(P9_12) + BONE_LED(P9_13) + BONE_LED(P9_14) + BONE_LED(P9_15) + BONE_LED(P9_16) + BONE_LED(P9_17) + BONE_LED(P9_18) + BONE_LED(P9_19) + BONE_LED(P9_20) + BONE_LED(P9_21) + BONE_LED(P9_22) + BONE_LED(P9_23) + BONE_LED(P9_24) + BONE_LED(P9_25) + BONE_LED(P9_26) + BONE_LED(P9_27) + BONE_LED(P9_28) + BONE_LED(P9_29) + BONE_LED(P9_30) + BONE_LED(P9_31) + + BONE_LED(P9_41) + BONE_LED(P9_91) + BONE_LED(P9_42) + BONE_LED(P9_92) + + BONE_LED(A15) + }; +}; + +// For dummy refrence when peripheral is not available. +&{/} { + not_available: not_available { + // Use ¬_available when required. + // This node is responsible to create these entries, + // /sys/firmware/devicetree/base/__symbols__/not_available + // /sys/firmware/devicetree/base/not_available + }; +}; + +// For compatible bone pinmuxing +bone_pinmux: &am33xx_pinmux { + emmc_pins: pinmux_emmc_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_CSN1, PIN_INPUT_PULLUP, MUX_MODE2) /* gpmc_csn1.mmc1_clk */ + AM33XX_PADCONF(AM335X_PIN_GPMC_CSN2, PIN_INPUT_PULLUP, MUX_MODE2) /* gpmc_csn2.mmc1_cmd */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD0, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad0.mmc1_dat0 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD1, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad1.mmc1_dat1 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD2, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad2.mmc1_dat2 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD3, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad3.mmc1_dat3 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD4, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad4.mmc1_dat4 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD5, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad5.mmc1_dat5 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD6, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad6.mmc1_dat6 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD7, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad7.mmc1_dat7 */ + >; + }; + + nxp_hdmi_bonelt_pins: nxp_hdmi_bonelt_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_OUTPUT_PULLDOWN, MUX_MODE3) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA0, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA1, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA2, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA3, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA4, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA5, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA6, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA7, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA8, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA9, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA10, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA11, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA12, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA13, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA14, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA15, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_VSYNC, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_HSYNC, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_PCLK, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_AC_BIAS_EN, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + >; + }; + + nxp_hdmi_bonelt_off_pins: nxp_hdmi_bonelt_off_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_OUTPUT_PULLDOWN, MUX_MODE3) + >; + }; + + mcasp0_pins: mcasp0_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKX, PIN_INPUT_PULLUP, MUX_MODE0) /* mcasp0_ahcklx.mcasp0_ahclkx */ + AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKR, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mcasp0_ahclkr.mcasp0_axr2*/ + AM33XX_PADCONF(AM335X_PIN_MCASP0_FSX, PIN_OUTPUT_PULLUP, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_MCASP0_ACLKX, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_GPMC_A11, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gpmc_a11.GPIO1_27 */ + >; + }; +}; + +// ADC +bone_adc: &tscadc { + +}; diff --git a/arch/arm/boot/dts/am335x-bone-common-univ.dtsi b/arch/arm/boot/dts/am335x-bone-common-univ.dtsi new file mode 100644 index 0000000000000..3af6f472c779e --- /dev/null +++ b/arch/arm/boot/dts/am335x-bone-common-univ.dtsi @@ -0,0 +1,2289 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#include + +&am33xx_pinmux { + +/* macro: BONE_PIN( , , */ +#define BONE_PIN(XX,ZZ,QQ) \ + XX##_##ZZ##_pin: pinmux_##XX##_##ZZ##_pin { pinctrl-single,pins = < QQ >; }; + + /************************/ + /* P8 Header */ + /************************/ + + /* P8_01 GND */ + + /* P8_02 GND */ + + + /* P8_03 (ZCZ ball R9) gpmc_ad6 (emmc) */ + BONE_PIN(P8_03, default, P8_03(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_03, gpio, P8_03(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_03, gpio_pu, P8_03(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_03, gpio_pd, P8_03(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + + /* P8_04 (ZCZ ball T9) gpmc_ad7 (emmc) */ + BONE_PIN(P8_04, default, P8_04(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_04, gpio, P8_04(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_04, gpio_pu, P8_04(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_04, gpio_pd, P8_04(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + + /* P8_05 (ZCZ ball R8) gpmc_ad2 (emmc) */ + BONE_PIN(P8_05, default, P8_05(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_05, gpio, P8_05(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_05, gpio_pu, P8_05(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_05, gpio_pd, P8_05(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + + /* P8_06 (ZCZ ball T8) gpmc_ad3 (emmc) */ + BONE_PIN(P8_06, default, P8_06(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_06, gpio, P8_06(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_06, gpio_pu, P8_06(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_06, gpio_pd, P8_06(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + + /* P8_07 (ZCZ ball R7) gpmc_advn_ale (gpio2_2) */ + BONE_PIN(P8_07, default, P8_07(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_07, gpio, P8_07(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_07, gpio_pu, P8_07(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_07, gpio_pd, P8_07(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_07, timer, P8_07(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + + /* P8_08 (ZCZ ball T7) gpmc_oen_ren (gpio2_3) */ + BONE_PIN(P8_08, default, P8_08(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_08, gpio, P8_08(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_08, gpio_pu, P8_08(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_08, gpio_pd, P8_08(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_08, timer, P8_08(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + + /* P8_09 (ZCZ ball T6) gpmc_be0n_cle (gpio2_5) */ + BONE_PIN(P8_09, default, P8_09(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_09, gpio, P8_09(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_09, gpio_pu, P8_09(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_09, gpio_pd, P8_09(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_09, timer, P8_09(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + + /* P8_10 (ZCZ ball U6) gpmc_wen (gpio2_4) */ + BONE_PIN(P8_10, default, P8_10(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_10, gpio, P8_10(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_10, gpio_pu, P8_10(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_10, gpio_pd, P8_10(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_10, timer, P8_10(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + + /* P8_11 (ZCZ ball R12) gpmc_ad13 (gpio1_13) */ + BONE_PIN(P8_11, default, P8_11(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_11, gpio, P8_11(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_11, gpio_pu, P8_11(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_11, gpio_pd, P8_11(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_11, eqep, P8_11(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + BONE_PIN(P8_11, pruout, P8_11(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6)) + + /* P8_12 (ZCZ ball T12) gpmc_ad12 (gpio1_12) */ + BONE_PIN(P8_12, default, P8_12(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_12, gpio, P8_12(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_12, gpio_pu, P8_12(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_12, gpio_pd, P8_12(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_12, eqep, P8_12(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + BONE_PIN(P8_12, pruout, P8_12(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6)) + + /* P8_13 (ZCZ ball T10) gpmc_ad9 (gpio0_23) */ + BONE_PIN(P8_13, default, P8_13(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_13, gpio, P8_13(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_13, gpio_pu, P8_13(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_13, gpio_pd, P8_13(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_13, pwm, P8_13(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE4)) + + /* P8_14 (ZCZ ball T11) gpmc_ad10 (gpio0_26) */ + BONE_PIN(P8_14, default, P8_14(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_14, gpio, P8_14(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_14, gpio_pu, P8_14(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_14, gpio_pd, P8_14(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_14, pwm, P8_14(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE4)) + + /* P8_15 (ZCZ ball U13) gpmc_ad15 (gpio1_15) */ + BONE_PIN(P8_15, default, P8_15(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_15, gpio, P8_15(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_15, gpio_pu, P8_15(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_15, gpio_pd, P8_15(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_15, eqep, P8_15(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + BONE_PIN(P8_15, pru_ecap_pwm, P8_15(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_15, pruin, P8_15(PIN_INPUT | MUX_MODE6)) + + /* P8_16 (ZCZ ball V13) gpmc_ad14 (gpio1_14) */ + BONE_PIN(P8_16, default, P8_16(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_16, gpio, P8_16(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_16, gpio_pu, P8_16(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_16, gpio_pd, P8_16(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_16, eqep, P8_16(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + BONE_PIN(P8_16, pruin, P8_16(PIN_INPUT | MUX_MODE6)) + + /* P8_17 (ZCZ ball U12) gpmc_ad11 (gpio0_27) */ + BONE_PIN(P8_17, default, P8_17(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_17, gpio, P8_17(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_17, gpio_pu, P8_17(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_17, gpio_pd, P8_17(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_17, pwm, P8_17(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE4)) + + /* P8_18 (ZCZ ball V12) gpmc_clk (gpio2_1) */ + BONE_PIN(P8_18, default, P8_18(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_18, gpio, P8_18(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_18, gpio_pu, P8_18(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_18, gpio_pd, P8_18(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + + /* P8_19 (ZCZ ball U10) gpmc_ad8 (gpio0_22) */ + BONE_PIN(P8_19, default, P8_19(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_19, gpio, P8_19(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_19, gpio_pu, P8_19(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_19, gpio_pd, P8_19(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_19, pwm, P8_19(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE4)) + + /* P8_20 (ZCZ ball V9) gpmc_csn2 (emmc) */ + BONE_PIN(P8_20, default, P8_20(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_20, gpio, P8_20(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_20, gpio_pu, P8_20(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_20, gpio_pd, P8_20(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_20, pruout, P8_20(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_20, pruin, P8_20(PIN_INPUT | MUX_MODE6)) + + /* P8_21 (ZCZ ball U9) gpmc_csn1 (emmc) */ + BONE_PIN(P8_21, default, P8_21(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_21, gpio, P8_21(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_21, gpio_pu, P8_21(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_21, gpio_pd, P8_21(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_21, pruout, P8_21(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_21, pruin, P8_21(PIN_INPUT | MUX_MODE6)) + + /* P8_22 (ZCZ ball V8) gpmc_ad5 (emmc) */ + BONE_PIN(P8_22, default, P8_22(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_22, gpio, P8_22(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_22, gpio_pu, P8_22(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_22, gpio_pd, P8_22(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + + /* P8_23 (ZCZ ball U8) gpmc_ad4 (emmc) */ + BONE_PIN(P8_23, default, P8_23(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_23, gpio, P8_23(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_23, gpio_pu, P8_23(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_23, gpio_pd, P8_23(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + + /* P8_24 (ZCZ ball V7) gpmc_ad1 (emmc) */ + BONE_PIN(P8_24, default, P8_24(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_24, gpio, P8_24(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_24, gpio_pu, P8_24(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_24, gpio_pd, P8_24(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + + /* P8_25 (ZCZ ball U7) gpmc_ad0 (emmc) */ + BONE_PIN(P8_25, default, P8_25(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_25, gpio, P8_25(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_25, gpio_pu, P8_25(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_25, gpio_pd, P8_25(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + + /* P8_26 (ZCZ ball V6) gpmc_csn0 (gpio1_29) */ + BONE_PIN(P8_26, default, P8_26(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_26, gpio, P8_26(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_26, gpio_pu, P8_26(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_26, gpio_pd, P8_26(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + + /* P8_27 (ZCZ ball U5) lcd_vsync (hdmi) */ + BONE_PIN(P8_27, default, P8_27(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_27, gpio, P8_27(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_27, gpio_pu, P8_27(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_27, gpio_pd, P8_27(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_27, pruout, P8_27(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_27, pruin, P8_27(PIN_INPUT | MUX_MODE6)) + + /* P8_28 (ZCZ ball V5) lcd_pclk (hdmi) */ + BONE_PIN(P8_28, default, P8_28(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_28, gpio, P8_28(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_28, gpio_pu, P8_28(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_28, gpio_pd, P8_28(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_28, pruout, P8_28(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_28, pruin, P8_28(PIN_INPUT | MUX_MODE6)) + + /* P8_29 (ZCZ ball R5) lcd_hsync (hdmi) */ + BONE_PIN(P8_29, default, P8_29(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_29, gpio, P8_29(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_29, gpio_pu, P8_29(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_29, gpio_pd, P8_29(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_29, pruout, P8_29(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_29, pruin, P8_29(PIN_INPUT | MUX_MODE6)) + + /* P8_30 (ZCZ ball R6) lcd_ac_bias_en (hdmi) */ + BONE_PIN(P8_30, default, P8_30(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_30, gpio, P8_30(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_30, gpio_pu, P8_30(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_30, gpio_pd, P8_30(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_30, pruout, P8_30(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_30, pruin, P8_30(PIN_INPUT | MUX_MODE6)) + + /* P8_31 (ZCZ ball V4) lcd_data14 (hdmi) */ + BONE_PIN(P8_31, default, P8_31(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_31, gpio, P8_31(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_31, gpio_pu, P8_31(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_31, gpio_pd, P8_31(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_31, eqep, P8_31(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + BONE_PIN(P8_31, uart, P8_31(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + + /* P8_32 (ZCZ ball T5) lcd_data15 (hdmi) */ + BONE_PIN(P8_32, default, P8_32(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_32, gpio, P8_32(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_32, gpio_pu, P8_32(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_32, gpio_pd, P8_32(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_32, eqep, P8_32(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + + /* P8_33 (ZCZ ball V3) lcd_data13 (hdmi) */ + BONE_PIN(P8_33, default, P8_33(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_33, gpio, P8_33(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_33, gpio_pu, P8_33(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_33, gpio_pd, P8_33(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_33, eqep, P8_33(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + + /* P8_34 (ZCZ ball U4) lcd_data11 (hdmi) */ + BONE_PIN(P8_34, default, P8_34(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_34, gpio, P8_34(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_34, gpio_pu, P8_34(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_34, gpio_pd, P8_34(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_34, pwm, P8_34(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE2)) + + /* P8_35 (ZCZ ball V2) lcd_data12 (hdmi) */ + BONE_PIN(P8_35, default, P8_35(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_35, gpio, P8_35(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_35, gpio_pu, P8_35(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_35, gpio_pd, P8_35(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_35, eqep, P8_35(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + + /* P8_36 (ZCZ ball U3) lcd_data10 (hdmi) */ + BONE_PIN(P8_36, default, P8_36(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_36, gpio, P8_36(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_36, gpio_pu, P8_36(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_36, gpio_pd, P8_36(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_36, pwm, P8_36(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE2)) + + /* P8_37 (ZCZ ball U1) lcd_data8 (hdmi) */ + BONE_PIN(P8_37, default, P8_37(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_37, gpio, P8_37(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_37, gpio_pu, P8_37(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_37, gpio_pd, P8_37(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_37, pwm, P8_37(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE2)) + BONE_PIN(P8_37, uart, P8_37(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + + /* P8_38 (ZCZ ball U2) lcd_data9 (hdmi) */ + BONE_PIN(P8_38, default, P8_38(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_38, gpio, P8_38(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_38, gpio_pu, P8_38(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_38, gpio_pd, P8_38(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_38, pwm, P8_38(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE2)) + BONE_PIN(P8_38, uart, P8_38(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + + /* P8_39 (ZCZ ball T3) lcd_data6 (hdmi) */ + BONE_PIN(P8_39, default, P8_39(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_39, gpio, P8_39(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_39, gpio_pu, P8_39(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_39, gpio_pd, P8_39(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_39, eqep, P8_39(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P8_39, pruout, P8_39(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_39, pruin, P8_39(PIN_INPUT | MUX_MODE6)) + + /* P8_40 (ZCZ ball T4) lcd_data7 (hdmi) */ + BONE_PIN(P8_40, default, P8_40(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_40, gpio, P8_40(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_40, gpio_pu, P8_40(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_40, gpio_pd, P8_40(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_40, eqep, P8_40(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P8_40, pruout, P8_40(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_40, pruin, P8_40(PIN_INPUT | MUX_MODE6)) + + /* P8_41 (ZCZ ball T1) lcd_data4 (hdmi) */ + BONE_PIN(P8_41, default, P8_41(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_41, gpio, P8_41(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_41, gpio_pu, P8_41(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_41, gpio_pd, P8_41(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_41, eqep, P8_41(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P8_41, pruout, P8_41(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_41, pruin, P8_41(PIN_INPUT | MUX_MODE6)) + + /* P8_42 (ZCZ ball T2) lcd_data5 (hdmi) */ + BONE_PIN(P8_42, default, P8_42(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_42, gpio, P8_42(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_42, gpio_pu, P8_42(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_42, gpio_pd, P8_42(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_42, eqep, P8_42(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P8_42, pruout, P8_42(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_42, pruin, P8_42(PIN_INPUT | MUX_MODE6)) + + /* P8_43 (ZCZ ball R3) lcd_data2 (hdmi) */ + BONE_PIN(P8_43, default, P8_43(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_43, gpio, P8_43(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_43, gpio_pu, P8_43(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_43, gpio_pd, P8_43(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_43, pwm, P8_43(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3)) + BONE_PIN(P8_43, pruout, P8_43(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_43, pruin, P8_43(PIN_INPUT | MUX_MODE6)) + + /* P8_44 (ZCZ ball R4) lcd_data3 (hdmi) */ + BONE_PIN(P8_44, default, P8_44(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_44, gpio, P8_44(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_44, gpio_pu, P8_44(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_44, gpio_pd, P8_44(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_44, pwm, P8_44(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3)) + BONE_PIN(P8_44, pruout, P8_44(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_44, pruin, P8_44(PIN_INPUT | MUX_MODE6)) + + /* P8_45 (ZCZ ball R1) lcd_data0 (hdmi) */ + BONE_PIN(P8_45, default, P8_45(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_45, gpio, P8_45(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_45, gpio_pu, P8_45(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_45, gpio_pd, P8_45(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_45, pwm, P8_45(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3)) + BONE_PIN(P8_45, pruout, P8_45(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_45, pruin, P8_45(PIN_INPUT | MUX_MODE6)) + + /* P8_46 (ZCZ ball R2) lcd_data1 (hdmi) */ + BONE_PIN(P8_46, default, P8_46(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_46, gpio, P8_46(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_46, gpio_pu, P8_46(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_46, gpio_pd, P8_46(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_46, pwm, P8_46(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3)) + BONE_PIN(P8_46, pruout, P8_46(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_46, pruin, P8_46(PIN_INPUT | MUX_MODE6)) + + /************************/ + /* P9 Header */ + /************************/ + + /* P9_01 GND */ + + /* P9_02 GND */ + + /* P9_03 3V3 */ + + /* P9_04 3V3 */ + + /* P9_05 VDD_5V */ + + /* P9_06 VDD_5V */ + + /* P9_07 SYS_5V */ + + /* P9_08 SYS_5V */ + + /* P9_09 PWR_BUT */ + + /* P9_10 RSTn */ + + /* P9_11 (ZCZ ball T17) gpmc_wait0 (gpio0_30) */ + BONE_PIN(P9_11, default, P9_11(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_11, gpio, P9_11(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_11, gpio_pu, P9_11(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_11, gpio_pd, P9_11(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_11, uart, P9_11(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE6)) + + /* P9_12 (ZCZ ball U18) gpmc_be1n (gpio1_28) */ + BONE_PIN(P9_12, default, P9_12(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_12, gpio, P9_12(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_12, gpio_pu, P9_12(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_12, gpio_pd, P9_12(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + + /* P9_13 (ZCZ ball U17) gpmc_wpn (gpio0_31) */ + BONE_PIN(P9_13, default, P9_13(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_13, gpio, P9_13(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_13, gpio_pu, P9_13(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_13, gpio_pd, P9_13(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_13, uart, P9_13(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE6)) + + /* P9_14 (ZCZ ball U14) gpmc_a2 (gpio1_18) */ + BONE_PIN(P9_14, default, P9_14(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_14, gpio, P9_14(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_14, gpio_pu, P9_14(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_14, gpio_pd, P9_14(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_14, pwm, P9_14(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6)) + + /* P9_15 (ZCZ ball R13) gpmc_a0 (gpio1_16) */ + BONE_PIN(P9_15, default, P9_15(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_15, gpio, P9_15(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_15, gpio_pu, P9_15(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_15, gpio_pd, P9_15(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_15, pwm, P9_15(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6)) + + /* P9_16 (ZCZ ball T14) gpmc_a3 (gpio1_19) */ + BONE_PIN(P9_16, default, P9_16(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_16, gpio, P9_16(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_16, gpio_pu, P9_16(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_16, gpio_pd, P9_16(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_16, pwm, P9_16(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6)) + + /* P9_17 (ZCZ ball A16) spi0_cs0 (gpio0_5) */ + BONE_PIN(P9_17, default, P9_17(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_17, gpio, P9_17(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_17, gpio_pu, P9_17(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_17, gpio_pd, P9_17(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_17, spi_cs, P9_17(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0)) + BONE_PIN(P9_17, i2c, P9_17(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + BONE_PIN(P9_17, pwm, P9_17(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3)) + BONE_PIN(P9_17, pru_uart, P9_17(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + + /* P9_18 (ZCZ ball B16) spi0_d1 (gpio0_4) */ + BONE_PIN(P9_18, default, P9_18(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_18, gpio, P9_18(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_18, gpio_pu, P9_18(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_18, gpio_pd, P9_18(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_18, spi, P9_18(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0)) + BONE_PIN(P9_18, i2c, P9_18(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + BONE_PIN(P9_18, pwm, P9_18(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3)) + BONE_PIN(P9_18, pru_uart, P9_18(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + + /* P9_19 (ZCZ ball D17) uart1_rtsn (i2c2_scl) */ + BONE_PIN(P9_19, default, P9_19(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P9_19, gpio, P9_19(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_19, gpio_pu, P9_19(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_19, gpio_pd, P9_19(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_19, timer, P9_19(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P9_19, can, P9_19(PIN_INPUT_PULLUP | MUX_MODE2)) + BONE_PIN(P9_19, i2c, P9_19(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P9_19, spi_cs, P9_19(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + BONE_PIN(P9_19, pru_uart, P9_19(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE5)) + + /* P9_20 (ZCZ ball D18) uart1_ctsn (i2c2_sda) */ + BONE_PIN(P9_20, default, P9_20(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P9_20, gpio, P9_20(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_20, gpio_pu, P9_20(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_20, gpio_pd, P9_20(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_20, timer, P9_20(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P9_20, can, P9_20(PIN_OUTPUT_PULLUP | MUX_MODE2)) + BONE_PIN(P9_20, i2c, P9_20(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P9_20, spi_cs, P9_20(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + BONE_PIN(P9_20, pru_uart, P9_20(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE5)) + + /* P9_21 (ZCZ ball B17) spi0_d0 (gpio0_3) */ + BONE_PIN(P9_21, default, P9_21(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_21, gpio, P9_21(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_21, gpio_pu, P9_21(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_21, gpio_pd, P9_21(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_21, spi, P9_21(PIN_INPUT_PULLUP | MUX_MODE0)) + BONE_PIN(P9_21, uart, P9_21(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P9_21, i2c, P9_21(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + BONE_PIN(P9_21, pwm, P9_21(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3)) + BONE_PIN(P9_21, pru_uart, P9_21(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + + /* P9_22 (ZCZ ball A17) spi0_sclk (gpio0_2) */ + BONE_PIN(P9_22, default, P9_22(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_22, gpio, P9_22(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_22, gpio_pu, P9_22(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_22, gpio_pd, P9_22(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_22, spi_sclk, P9_22(PIN_INPUT_PULLUP | MUX_MODE0)) + BONE_PIN(P9_22, uart, P9_22(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P9_22, i2c, P9_22(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + BONE_PIN(P9_22, pwm, P9_22(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3)) + BONE_PIN(P9_22, pru_uart, P9_22(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + + /* P9_23 (ZCZ ball V14) gpmc_a1 (gpio1_17) */ + BONE_PIN(P9_23, default, P9_23(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_23, gpio, P9_23(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_23, gpio_pu, P9_23(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_23, gpio_pd, P9_23(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_23, pwm, P9_23(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6)) + + /* P9_24 (ZCZ ball D15) uart1_txd (gpio0_15) */ + BONE_PIN(P9_24, default, P9_24(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_24, gpio, P9_24(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_24, gpio_pu, P9_24(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_24, gpio_pd, P9_24(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_24, uart, P9_24(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0)) + BONE_PIN(P9_24, can, P9_24(PIN_INPUT_PULLUP | MUX_MODE2)) + BONE_PIN(P9_24, i2c, P9_24(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P9_24, pru_uart, P9_24(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE5)) + BONE_PIN(P9_24, pruin, P9_24(PIN_INPUT | MUX_MODE6)) + + /* P9_25 (ZCZ ball A14) mcasp0_ahclkx (audio) */ + BONE_PIN(P9_25, default, P9_25(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_25, gpio, P9_25(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_25, gpio_pu, P9_25(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_25, gpio_pd, P9_25(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_25, eqep, P9_25(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P9_25, pruout, P9_25(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P9_25, pruin, P9_25(PIN_INPUT | MUX_MODE6)) + + /* P9_26 (ZCZ ball D16) uart1_rxd (gpio0_14) */ + BONE_PIN(P9_26, default, P9_26(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_26, gpio, P9_26(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_26, gpio_pu, P9_26(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_26, gpio_pd, P9_26(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_26, uart, P9_26(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0)) + BONE_PIN(P9_26, can, P9_26(PIN_OUTPUT_PULLUP | MUX_MODE2)) + BONE_PIN(P9_26, i2c, P9_26(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P9_26, pru_uart, P9_26(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE5)) + BONE_PIN(P9_26, pruin, P9_26(PIN_INPUT | MUX_MODE6)) + + /* P9_27 (ZCZ ball C13) mcasp0_fsr (gpio3_19) */ + BONE_PIN(P9_27, default, P9_27(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_27, gpio, P9_27(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_27, gpio_pu, P9_27(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_27, gpio_pd, P9_27(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_27, eqep, P9_27(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P9_27, pruout, P9_27(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P9_27, pruin, P9_27(PIN_INPUT | MUX_MODE6)) + + /* P9_28 (ZCZ ball C12) mcasp0_ahclkr (audio) */ + BONE_PIN(P9_28, default, P9_28(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_28, gpio, P9_28(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_28, gpio_pu, P9_28(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_28, gpio_pd, P9_28(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_28, pwm, P9_28(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE1)) + BONE_PIN(P9_28, spi_cs, P9_28(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P9_28, pwm2, P9_28(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE4)) + BONE_PIN(P9_28, pruout, P9_28(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P9_28, pruin, P9_28(PIN_INPUT | MUX_MODE6)) + + /* P9_29 (ZCZ ball B13) mcasp0_fsx (audio) */ + BONE_PIN(P9_29, default, P9_29(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_29, gpio, P9_29(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_29, gpio_pu, P9_29(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_29, gpio_pd, P9_29(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_29, pwm, P9_29(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE1)) + BONE_PIN(P9_29, spi, P9_29(PIN_INPUT_PULLUP | MUX_MODE3)) + BONE_PIN(P9_29, pruout, P9_29(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P9_29, pruin, P9_29(PIN_INPUT | MUX_MODE6)) + + /* P9_30 (ZCZ ball D12) mcasp0_axr0 (gpio3_16) */ + BONE_PIN(P9_30, default, P9_30(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_30, gpio, P9_30(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_30, gpio_pu, P9_30(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_30, gpio_pd, P9_30(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_30, pwm, P9_30(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE1)) + BONE_PIN(P9_30, spi, P9_30(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P9_30, pruout, P9_30(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P9_30, pruin, P9_30(PIN_INPUT | MUX_MODE6)) + + /* P9_31 (ZCZ ball A13) mcasp0_aclkx (audio) */ + BONE_PIN(P9_31, default, P9_31(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_31, gpio, P9_31(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_31, gpio_pu, P9_31(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_31, gpio_pd, P9_31(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_31, pwm, P9_31(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE1)) + BONE_PIN(P9_31, spi_sclk, P9_31(PIN_INPUT_PULLUP | MUX_MODE3)) + BONE_PIN(P9_31, pruout, P9_31(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P9_31, pruin, P9_31(PIN_INPUT | MUX_MODE6)) + + /* P9_32 VADC */ + + /* P9_33 (ZCZ ball C8) AIN4 */ + + /* P9_34 AGND */ + + /* P9_35 (ZCZ ball A8) AIN6 */ + + /* P9_36 (ZCZ ball B8) AIN5 */ + + /* P9_37 (ZCZ ball B7) AIN2 */ + + /* P9_38 (ZCZ ball A7) AIN3 */ + + /* P9_39 (ZCZ ball B6) AIN0 */ + + /* P9_40 (ZCZ ball C7) AIN1 */ + + /* P9_41 (ZCZ ball D14) xdma_event_intr1 (gpio0_20) */ + BONE_PIN(P9_41, default, P9_41(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_41, gpio, P9_41(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_41, gpio_pu, P9_41(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_41, gpio_pd, P9_41(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_41, timer, P9_41(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + BONE_PIN(P9_41, pruin, P9_41(PIN_INPUT | MUX_MODE5)) + + /* P9_41.1 */ + /* P9_91 (ZCZ ball D13) mcasp0_axr1 (gpio3_20) */ + BONE_PIN(P9_91, default, P9_91(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_91, gpio, P9_91(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_91, gpio_pu, P9_91(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_91, gpio_pd, P9_91(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_91, eqep, P9_91(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P9_91, pruout, P9_91(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P9_91, pruin, P9_91(PIN_INPUT | MUX_MODE6)) + + /* P9_42 (ZCZ ball C18) eCAP0_in_PWM0_out (gpio0_7) */ + BONE_PIN(P9_42, default, P9_42(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_42, gpio, P9_42(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_42, gpio_pu, P9_42(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_42, gpio_pd, P9_42(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_42, pwm, P9_42(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE0)) + BONE_PIN(P9_42, uart, P9_42(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P9_42, spi_cs, P9_42(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + BONE_PIN(P9_42, pru_ecap_pwm, P9_42(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3)) + BONE_PIN(P9_42, spi_sclk, P9_42(PIN_INPUT_PULLUP | MUX_MODE4)) + + /* P9_42.1 */ + /* P9_92 (ZCZ ball B12) mcasp0_aclkr (gpio3_18) */ + BONE_PIN(P9_92, default, P9_92(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_92, gpio, P9_92(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_92, gpio_pu, P9_92(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_92, gpio_pd, P9_92(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_92, eqep, P9_92(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P9_92, pruout, P9_92(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P9_92, pruin, P9_92(PIN_INPUT | MUX_MODE6)) + + /* P9_43 GND */ + + /* P9_44 GND */ + + /* P9_45 GND */ + + /* P9_46 GND */ +}; + +&i2c1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + + clock-frequency = <100000>; + symlink = "bone/i2c/1"; +}; + +&i2c2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + + clock-frequency = <100000>; + symlink = "bone/i2c/2"; +}; + +&uart1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/uart/1"; +}; + +&uart2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/uart/2"; +}; + +&uart3 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/uart/3"; +}; + +&uart4 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/uart/4"; +}; + +&uart5 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/uart/5"; +}; + +&dcan0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/can/0"; +}; + +&dcan1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/can/1"; +}; + +&eqep0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/eqep/0"; +}; + +&eqep1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/eqep/1"; +}; + +&eqep2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/eqep/2"; +}; + +&epwmss0 { + status = "okay"; +}; + +&epwmss1 { + status = "okay"; +}; + +&epwmss2 { + status = "okay"; +}; + +&ehrpwm0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + +&ehrpwm1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + +&ehrpwm2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + +&ecap0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + +&ecap1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + +&ecap2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + +&spi0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + + channel@0 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "spidev"; + symlink = "bone/spi/0.0"; + + reg = <0>; + spi-max-frequency = <16000000>; + spi-cpha; + }; + + channel@1 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "spidev"; + symlink = "bone/spi/0.1"; + + reg = <1>; + spi-max-frequency = <16000000>; + }; +}; + +&spi1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + + channel@0 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "spidev"; + symlink = "bone/spi/1.0"; + + reg = <0>; + spi-max-frequency = <16000000>; + spi-cpha; + }; + + channel@1 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "spidev"; + symlink = "bone/spi/1.1"; + + reg = <1>; + spi-max-frequency = <16000000>; + }; +}; + +/**********************************************************************/ +/* Pin Multiplex Helpers */ +/* */ +/* These provide userspace runtime pin configuration for the */ +/* BeagleBone cape expansion headers */ +/**********************************************************************/ + +&ocp { + /************************/ + /* P8 Header */ + /************************/ + + /* P8_01 GND */ + + /* P8_02 GND */ + + + /* P8_03 (ZCZ ball R9) emmc */ + P8_03_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd"; + pinctrl-0 = <&P8_03_default_pin>; + pinctrl-1 = <&P8_03_gpio_pin>; + pinctrl-2 = <&P8_03_gpio_pu_pin>; + pinctrl-3 = <&P8_03_gpio_pd_pin>; + }; + + /* P8_04 (ZCZ ball T9) emmc */ + P8_04_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd"; + pinctrl-0 = <&P8_04_default_pin>; + pinctrl-1 = <&P8_04_gpio_pin>; + pinctrl-2 = <&P8_04_gpio_pu_pin>; + pinctrl-3 = <&P8_04_gpio_pd_pin>; + }; + + /* P8_05 (ZCZ ball R8) emmc */ + P8_05_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd"; + pinctrl-0 = <&P8_05_default_pin>; + pinctrl-1 = <&P8_05_gpio_pin>; + pinctrl-2 = <&P8_05_gpio_pu_pin>; + pinctrl-3 = <&P8_05_gpio_pd_pin>; + }; + + /* P8_06 (ZCZ ball T8) emmc */ + P8_06_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd"; + pinctrl-0 = <&P8_06_default_pin>; + pinctrl-1 = <&P8_06_gpio_pin>; + pinctrl-2 = <&P8_06_gpio_pu_pin>; + pinctrl-3 = <&P8_06_gpio_pd_pin>; + }; + + /* P8_07 (ZCZ ball R7) */ + P8_07_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "timer"; + pinctrl-0 = <&P8_07_default_pin>; + pinctrl-1 = <&P8_07_gpio_pin>; + pinctrl-2 = <&P8_07_gpio_pu_pin>; + pinctrl-3 = <&P8_07_gpio_pd_pin>; + pinctrl-4 = <&P8_07_timer_pin>; + }; + + /* P8_08 (ZCZ ball T7) */ + P8_08_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "timer"; + pinctrl-0 = <&P8_08_default_pin>; + pinctrl-1 = <&P8_08_gpio_pin>; + pinctrl-2 = <&P8_08_gpio_pu_pin>; + pinctrl-3 = <&P8_08_gpio_pd_pin>; + pinctrl-4 = <&P8_08_timer_pin>; + }; + + /* P8_09 (ZCZ ball T6) */ + P8_09_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "timer"; + pinctrl-0 = <&P8_09_default_pin>; + pinctrl-1 = <&P8_09_gpio_pin>; + pinctrl-2 = <&P8_09_gpio_pu_pin>; + pinctrl-3 = <&P8_09_gpio_pd_pin>; + pinctrl-4 = <&P8_09_timer_pin>; + }; + + /* P8_10 (ZCZ ball U6) */ + P8_10_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "timer"; + pinctrl-0 = <&P8_10_default_pin>; + pinctrl-1 = <&P8_10_gpio_pin>; + pinctrl-2 = <&P8_10_gpio_pu_pin>; + pinctrl-3 = <&P8_10_gpio_pd_pin>; + pinctrl-4 = <&P8_10_timer_pin>; + }; + + /* P8_11 (ZCZ ball R12) */ + P8_11_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout"; + pinctrl-0 = <&P8_11_default_pin>; + pinctrl-1 = <&P8_11_gpio_pin>; + pinctrl-2 = <&P8_11_gpio_pu_pin>; + pinctrl-3 = <&P8_11_gpio_pd_pin>; + pinctrl-4 = <&P8_11_eqep_pin>; + pinctrl-5 = <&P8_11_pruout_pin>; + }; + + /* P8_12 (ZCZ ball T12) */ + P8_12_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout"; + pinctrl-0 = <&P8_12_default_pin>; + pinctrl-1 = <&P8_12_gpio_pin>; + pinctrl-2 = <&P8_12_gpio_pu_pin>; + pinctrl-3 = <&P8_12_gpio_pd_pin>; + pinctrl-4 = <&P8_12_eqep_pin>; + pinctrl-5 = <&P8_12_pruout_pin>; + }; + + /* P8_13 (ZCZ ball T10) */ + P8_13_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P8_13_default_pin>; + pinctrl-1 = <&P8_13_gpio_pin>; + pinctrl-2 = <&P8_13_gpio_pu_pin>; + pinctrl-3 = <&P8_13_gpio_pd_pin>; + pinctrl-4 = <&P8_13_pwm_pin>; + }; + + /* P8_14 (ZCZ ball T11) */ + P8_14_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P8_14_default_pin>; + pinctrl-1 = <&P8_14_gpio_pin>; + pinctrl-2 = <&P8_14_gpio_pu_pin>; + pinctrl-3 = <&P8_14_gpio_pd_pin>; + pinctrl-4 = <&P8_14_pwm_pin>; + }; + + /* P8_15 (ZCZ ball U13) */ + P8_15_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pru_ecap_pwm", "pruin"; + pinctrl-0 = <&P8_15_default_pin>; + pinctrl-1 = <&P8_15_gpio_pin>; + pinctrl-2 = <&P8_15_gpio_pu_pin>; + pinctrl-3 = <&P8_15_gpio_pd_pin>; + pinctrl-4 = <&P8_15_eqep_pin>; + pinctrl-5 = <&P8_15_pru_ecap_pwm_pin>; + pinctrl-6 = <&P8_15_pruin_pin>; + }; + + /* P8_16 (ZCZ ball V13) */ + P8_16_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruin"; + pinctrl-0 = <&P8_16_default_pin>; + pinctrl-1 = <&P8_16_gpio_pin>; + pinctrl-2 = <&P8_16_gpio_pu_pin>; + pinctrl-3 = <&P8_16_gpio_pd_pin>; + pinctrl-4 = <&P8_16_eqep_pin>; + pinctrl-5 = <&P8_16_pruin_pin>; + }; + + /* P8_17 (ZCZ ball U12) */ + P8_17_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P8_17_default_pin>; + pinctrl-1 = <&P8_17_gpio_pin>; + pinctrl-2 = <&P8_17_gpio_pu_pin>; + pinctrl-3 = <&P8_17_gpio_pd_pin>; + pinctrl-4 = <&P8_17_pwm_pin>; + }; + + /* P8_18 (ZCZ ball V12) */ + P8_18_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd"; + pinctrl-0 = <&P8_18_default_pin>; + pinctrl-1 = <&P8_18_gpio_pin>; + pinctrl-2 = <&P8_18_gpio_pu_pin>; + pinctrl-3 = <&P8_18_gpio_pd_pin>; + }; + + /* P8_19 (ZCZ ball U10) */ + P8_19_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P8_19_default_pin>; + pinctrl-1 = <&P8_19_gpio_pin>; + pinctrl-2 = <&P8_19_gpio_pu_pin>; + pinctrl-3 = <&P8_19_gpio_pd_pin>; + pinctrl-4 = <&P8_19_pwm_pin>; + }; + + /* P8_20 (ZCZ ball V9) emmc */ + P8_20_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_20_default_pin>; + pinctrl-1 = <&P8_20_gpio_pin>; + pinctrl-2 = <&P8_20_gpio_pu_pin>; + pinctrl-3 = <&P8_20_gpio_pd_pin>; + pinctrl-4 = <&P8_20_pruout_pin>; + pinctrl-5 = <&P8_20_pruin_pin>; + }; + + /* P8_21 (ZCZ ball U9) emmc */ + P8_21_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_21_default_pin>; + pinctrl-1 = <&P8_21_gpio_pin>; + pinctrl-2 = <&P8_21_gpio_pu_pin>; + pinctrl-3 = <&P8_21_gpio_pd_pin>; + pinctrl-4 = <&P8_21_pruout_pin>; + pinctrl-5 = <&P8_21_pruin_pin>; + }; + + /* P8_22 (ZCZ ball V8) emmc */ + P8_22_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd"; + pinctrl-0 = <&P8_22_default_pin>; + pinctrl-1 = <&P8_22_gpio_pin>; + pinctrl-2 = <&P8_22_gpio_pu_pin>; + pinctrl-3 = <&P8_22_gpio_pd_pin>; + }; + + /* P8_23 (ZCZ ball U8) emmc */ + P8_23_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd"; + pinctrl-0 = <&P8_23_default_pin>; + pinctrl-1 = <&P8_23_gpio_pin>; + pinctrl-2 = <&P8_23_gpio_pu_pin>; + pinctrl-3 = <&P8_23_gpio_pd_pin>; + }; + + /* P8_24 (ZCZ ball V7) emmc */ + P8_24_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd"; + pinctrl-0 = <&P8_24_default_pin>; + pinctrl-1 = <&P8_24_gpio_pin>; + pinctrl-2 = <&P8_24_gpio_pu_pin>; + pinctrl-3 = <&P8_24_gpio_pd_pin>; + }; + + /* P8_25 (ZCZ ball U7) emmc */ + P8_25_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd"; + pinctrl-0 = <&P8_25_default_pin>; + pinctrl-1 = <&P8_25_gpio_pin>; + pinctrl-2 = <&P8_25_gpio_pu_pin>; + pinctrl-3 = <&P8_25_gpio_pd_pin>; + }; + + /* P8_26 (ZCZ ball V6) */ + P8_26_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd"; + pinctrl-0 = <&P8_26_default_pin>; + pinctrl-1 = <&P8_26_gpio_pin>; + pinctrl-2 = <&P8_26_gpio_pu_pin>; + pinctrl-3 = <&P8_26_gpio_pd_pin>; + }; + + /* P8_27 (ZCZ ball U5) hdmi */ + P8_27_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_27_default_pin>; + pinctrl-1 = <&P8_27_gpio_pin>; + pinctrl-2 = <&P8_27_gpio_pu_pin>; + pinctrl-3 = <&P8_27_gpio_pd_pin>; + pinctrl-4 = <&P8_27_pruout_pin>; + pinctrl-5 = <&P8_27_pruin_pin>; + }; + + /* P8_28 (ZCZ ball V5) hdmi */ + P8_28_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_28_default_pin>; + pinctrl-1 = <&P8_28_gpio_pin>; + pinctrl-2 = <&P8_28_gpio_pu_pin>; + pinctrl-3 = <&P8_28_gpio_pd_pin>; + pinctrl-4 = <&P8_28_pruout_pin>; + pinctrl-5 = <&P8_28_pruin_pin>; + }; + + /* P8_29 (ZCZ ball R5) hdmi */ + P8_29_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_29_default_pin>; + pinctrl-1 = <&P8_29_gpio_pin>; + pinctrl-2 = <&P8_29_gpio_pu_pin>; + pinctrl-3 = <&P8_29_gpio_pd_pin>; + pinctrl-4 = <&P8_29_pruout_pin>; + pinctrl-5 = <&P8_29_pruin_pin>; + }; + + /* P8_30 (ZCZ ball R6) hdmi */ + P8_30_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_30_default_pin>; + pinctrl-1 = <&P8_30_gpio_pin>; + pinctrl-2 = <&P8_30_gpio_pu_pin>; + pinctrl-3 = <&P8_30_gpio_pd_pin>; + pinctrl-4 = <&P8_30_pruout_pin>; + pinctrl-5 = <&P8_30_pruin_pin>; + }; + + /* P8_31 (ZCZ ball V4) hdmi */ + P8_31_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart", "eqep"; + pinctrl-0 = <&P8_31_default_pin>; + pinctrl-1 = <&P8_31_gpio_pin>; + pinctrl-2 = <&P8_31_gpio_pu_pin>; + pinctrl-3 = <&P8_31_gpio_pd_pin>; + pinctrl-4 = <&P8_31_uart_pin>; + pinctrl-5 = <&P8_31_eqep_pin>; + }; + + /* P8_32 (ZCZ ball T5) hdmi */ + P8_32_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep"; + pinctrl-0 = <&P8_32_default_pin>; + pinctrl-1 = <&P8_32_gpio_pin>; + pinctrl-2 = <&P8_32_gpio_pu_pin>; + pinctrl-3 = <&P8_32_gpio_pd_pin>; + pinctrl-4 = <&P8_32_eqep_pin>; + }; + + /* P8_33 (ZCZ ball V3) hdmi */ + P8_33_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep"; + pinctrl-0 = <&P8_33_default_pin>; + pinctrl-1 = <&P8_33_gpio_pin>; + pinctrl-2 = <&P8_33_gpio_pu_pin>; + pinctrl-3 = <&P8_33_gpio_pd_pin>; + pinctrl-4 = <&P8_33_eqep_pin>; + }; + + /* P8_34 (ZCZ ball U4) hdmi */ + P8_34_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P8_34_default_pin>; + pinctrl-1 = <&P8_34_gpio_pin>; + pinctrl-2 = <&P8_34_gpio_pu_pin>; + pinctrl-3 = <&P8_34_gpio_pd_pin>; + pinctrl-4 = <&P8_34_pwm_pin>; + }; + + /* P8_35 (ZCZ ball V2) hdmi */ + P8_35_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep"; + pinctrl-0 = <&P8_35_default_pin>; + pinctrl-1 = <&P8_35_gpio_pin>; + pinctrl-2 = <&P8_35_gpio_pu_pin>; + pinctrl-3 = <&P8_35_gpio_pd_pin>; + pinctrl-4 = <&P8_35_eqep_pin>; + }; + + /* P8_36 (ZCZ ball U3) hdmi */ + P8_36_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P8_36_default_pin>; + pinctrl-1 = <&P8_36_gpio_pin>; + pinctrl-2 = <&P8_36_gpio_pu_pin>; + pinctrl-3 = <&P8_36_gpio_pd_pin>; + pinctrl-4 = <&P8_36_pwm_pin>; + }; + + /* P8_37 (ZCZ ball U1) hdmi */ + P8_37_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart", "pwm"; + pinctrl-0 = <&P8_37_default_pin>; + pinctrl-1 = <&P8_37_gpio_pin>; + pinctrl-2 = <&P8_37_gpio_pu_pin>; + pinctrl-3 = <&P8_37_gpio_pd_pin>; + pinctrl-4 = <&P8_37_uart_pin>; + pinctrl-5 = <&P8_37_pwm_pin>; + }; + + /* P8_38 (ZCZ ball U2) hdmi */ + P8_38_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart", "pwm"; + pinctrl-0 = <&P8_38_default_pin>; + pinctrl-1 = <&P8_38_gpio_pin>; + pinctrl-2 = <&P8_38_gpio_pu_pin>; + pinctrl-3 = <&P8_38_gpio_pd_pin>; + pinctrl-4 = <&P8_38_uart_pin>; + pinctrl-5 = <&P8_38_pwm_pin>; + }; + + /* P8_39 (ZCZ ball T3) hdmi */ + P8_39_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout", "pruin"; + pinctrl-0 = <&P8_39_default_pin>; + pinctrl-1 = <&P8_39_gpio_pin>; + pinctrl-2 = <&P8_39_gpio_pu_pin>; + pinctrl-3 = <&P8_39_gpio_pd_pin>; + pinctrl-4 = <&P8_39_eqep_pin>; + pinctrl-5 = <&P8_39_pruout_pin>; + pinctrl-6 = <&P8_39_pruin_pin>; + }; + + /* P8_40 (ZCZ ball T4) hdmi */ + P8_40_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout", "pruin"; + pinctrl-0 = <&P8_40_default_pin>; + pinctrl-1 = <&P8_40_gpio_pin>; + pinctrl-2 = <&P8_40_gpio_pu_pin>; + pinctrl-3 = <&P8_40_gpio_pd_pin>; + pinctrl-4 = <&P8_40_eqep_pin>; + pinctrl-5 = <&P8_40_pruout_pin>; + pinctrl-6 = <&P8_40_pruin_pin>; + }; + + /* P8_41 (ZCZ ball T1) hdmi */ + P8_41_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout", "pruin"; + pinctrl-0 = <&P8_41_default_pin>; + pinctrl-1 = <&P8_41_gpio_pin>; + pinctrl-2 = <&P8_41_gpio_pu_pin>; + pinctrl-3 = <&P8_41_gpio_pd_pin>; + pinctrl-4 = <&P8_41_eqep_pin>; + pinctrl-5 = <&P8_41_pruout_pin>; + pinctrl-6 = <&P8_41_pruin_pin>; + }; + + /* P8_42 (ZCZ ball T2) hdmi */ + P8_42_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout", "pruin"; + pinctrl-0 = <&P8_42_default_pin>; + pinctrl-1 = <&P8_42_gpio_pin>; + pinctrl-2 = <&P8_42_gpio_pu_pin>; + pinctrl-3 = <&P8_42_gpio_pd_pin>; + pinctrl-4 = <&P8_42_eqep_pin>; + pinctrl-5 = <&P8_42_pruout_pin>; + pinctrl-6 = <&P8_42_pruin_pin>; + }; + + /* P8_43 (ZCZ ball R3) hdmi */ + P8_43_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P8_43_default_pin>; + pinctrl-1 = <&P8_43_gpio_pin>; + pinctrl-2 = <&P8_43_gpio_pu_pin>; + pinctrl-3 = <&P8_43_gpio_pd_pin>; + pinctrl-4 = <&P8_43_pwm_pin>; + pinctrl-5 = <&P8_43_pruout_pin>; + pinctrl-6 = <&P8_43_pruin_pin>; + }; + + /* P8_44 (ZCZ ball R4) hdmi */ + P8_44_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P8_44_default_pin>; + pinctrl-1 = <&P8_44_gpio_pin>; + pinctrl-2 = <&P8_44_gpio_pu_pin>; + pinctrl-3 = <&P8_44_gpio_pd_pin>; + pinctrl-4 = <&P8_44_pwm_pin>; + pinctrl-5 = <&P8_44_pruout_pin>; + pinctrl-6 = <&P8_44_pruin_pin>; + }; + + /* P8_45 (ZCZ ball R1) hdmi */ + P8_45_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P8_45_default_pin>; + pinctrl-1 = <&P8_45_gpio_pin>; + pinctrl-2 = <&P8_45_gpio_pu_pin>; + pinctrl-3 = <&P8_45_gpio_pd_pin>; + pinctrl-4 = <&P8_45_pwm_pin>; + pinctrl-5 = <&P8_45_pruout_pin>; + pinctrl-6 = <&P8_45_pruin_pin>; + }; + + /* P8_46 (ZCZ ball R2) hdmi */ + P8_46_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P8_46_default_pin>; + pinctrl-1 = <&P8_46_gpio_pin>; + pinctrl-2 = <&P8_46_gpio_pu_pin>; + pinctrl-3 = <&P8_46_gpio_pd_pin>; + pinctrl-4 = <&P8_46_pwm_pin>; + pinctrl-5 = <&P8_46_pruout_pin>; + pinctrl-6 = <&P8_46_pruin_pin>; + }; + + /************************/ + /* P9 Header */ + /************************/ + + /* P9_01 GND */ + + /* P9_02 GND */ + + /* P9_03 3V3 */ + + /* P9_04 3V3 */ + + /* P9_05 VDD_5V */ + + /* P9_06 VDD_5V */ + + /* P9_07 SYS_5V */ + + /* P9_08 SYS_5V */ + + /* P9_09 PWR_BUT */ + + /* P9_10 RSTn */ + + /* P9_11 (ZCZ ball T17) */ + P9_11_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart"; + pinctrl-0 = <&P9_11_default_pin>; + pinctrl-1 = <&P9_11_gpio_pin>; + pinctrl-2 = <&P9_11_gpio_pu_pin>; + pinctrl-3 = <&P9_11_gpio_pd_pin>; + pinctrl-4 = <&P9_11_uart_pin>; + }; + + /* P9_12 (ZCZ ball U18) */ + P9_12_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd"; + pinctrl-0 = <&P9_12_default_pin>; + pinctrl-1 = <&P9_12_gpio_pin>; + pinctrl-2 = <&P9_12_gpio_pu_pin>; + pinctrl-3 = <&P9_12_gpio_pd_pin>; + }; + + /* P9_13 (ZCZ ball U17) */ + P9_13_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart"; + pinctrl-0 = <&P9_13_default_pin>; + pinctrl-1 = <&P9_13_gpio_pin>; + pinctrl-2 = <&P9_13_gpio_pu_pin>; + pinctrl-3 = <&P9_13_gpio_pd_pin>; + pinctrl-4 = <&P9_13_uart_pin>; + }; + + /* P9_14 (ZCZ ball U14) */ + P9_14_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P9_14_default_pin>; + pinctrl-1 = <&P9_14_gpio_pin>; + pinctrl-2 = <&P9_14_gpio_pu_pin>; + pinctrl-3 = <&P9_14_gpio_pd_pin>; + pinctrl-4 = <&P9_14_pwm_pin>; + }; + + /* P9_15 (ZCZ ball R13) */ + P9_15_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P9_15_default_pin>; + pinctrl-1 = <&P9_15_gpio_pin>; + pinctrl-2 = <&P9_15_gpio_pu_pin>; + pinctrl-3 = <&P9_15_gpio_pd_pin>; + pinctrl-4 = <&P9_15_pwm_pin>; + }; + + /* P9_16 (ZCZ ball T14) */ + P9_16_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P9_16_default_pin>; + pinctrl-1 = <&P9_16_gpio_pin>; + pinctrl-2 = <&P9_16_gpio_pu_pin>; + pinctrl-3 = <&P9_16_gpio_pd_pin>; + pinctrl-4 = <&P9_16_pwm_pin>; + }; + + /* P9_17 (ZCZ ball A16) */ + P9_17_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_cs", "i2c", "pwm", "pru_uart"; + pinctrl-0 = <&P9_17_default_pin>; + pinctrl-1 = <&P9_17_gpio_pin>; + pinctrl-2 = <&P9_17_gpio_pu_pin>; + pinctrl-3 = <&P9_17_gpio_pd_pin>; + pinctrl-4 = <&P9_17_spi_cs_pin>; + pinctrl-5 = <&P9_17_i2c_pin>; + pinctrl-6 = <&P9_17_pwm_pin>; + pinctrl-7 = <&P9_17_pru_uart_pin>; + }; + + /* P9_18 (ZCZ ball B16) */ + P9_18_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi", "i2c", "pwm", "pru_uart"; + pinctrl-0 = <&P9_18_default_pin>; + pinctrl-1 = <&P9_18_gpio_pin>; + pinctrl-2 = <&P9_18_gpio_pu_pin>; + pinctrl-3 = <&P9_18_gpio_pd_pin>; + pinctrl-4 = <&P9_18_spi_pin>; + pinctrl-5 = <&P9_18_i2c_pin>; + pinctrl-6 = <&P9_18_pwm_pin>; + pinctrl-7 = <&P9_18_pru_uart_pin>; + }; + + /* P9_19 (ZCZ ball D17) i2c */ + P9_19_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_cs", "can", "i2c", "pru_uart", "timer"; + pinctrl-0 = <&P9_19_default_pin>; + pinctrl-1 = <&P9_19_gpio_pin>; + pinctrl-2 = <&P9_19_gpio_pu_pin>; + pinctrl-3 = <&P9_19_gpio_pd_pin>; + pinctrl-4 = <&P9_19_spi_cs_pin>; + pinctrl-5 = <&P9_19_can_pin>; + pinctrl-6 = <&P9_19_i2c_pin>; + pinctrl-7 = <&P9_19_pru_uart_pin>; + pinctrl-8 = <&P9_19_timer_pin>; + }; + + /* P9_20 (ZCZ ball D18) i2c */ + P9_20_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_cs", "can", "i2c", "pru_uart", "timer"; + pinctrl-0 = <&P9_20_default_pin>; + pinctrl-1 = <&P9_20_gpio_pin>; + pinctrl-2 = <&P9_20_gpio_pu_pin>; + pinctrl-3 = <&P9_20_gpio_pd_pin>; + pinctrl-4 = <&P9_20_spi_cs_pin>; + pinctrl-5 = <&P9_20_can_pin>; + pinctrl-6 = <&P9_20_i2c_pin>; + pinctrl-7 = <&P9_20_pru_uart_pin>; + pinctrl-8 = <&P9_20_timer_pin>; + }; + + /* P9_21 (ZCZ ball B17) */ + P9_21_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi", "uart", "i2c", "pwm", "pru_uart"; + pinctrl-0 = <&P9_21_default_pin>; + pinctrl-1 = <&P9_21_gpio_pin>; + pinctrl-2 = <&P9_21_gpio_pu_pin>; + pinctrl-3 = <&P9_21_gpio_pd_pin>; + pinctrl-4 = <&P9_21_spi_pin>; + pinctrl-5 = <&P9_21_uart_pin>; + pinctrl-6 = <&P9_21_i2c_pin>; + pinctrl-7 = <&P9_21_pwm_pin>; + pinctrl-8 = <&P9_21_pru_uart_pin>; + }; + + /* P9_22 (ZCZ ball A17) */ + P9_22_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_sclk", "uart", "i2c", "pwm", "pru_uart"; + pinctrl-0 = <&P9_22_default_pin>; + pinctrl-1 = <&P9_22_gpio_pin>; + pinctrl-2 = <&P9_22_gpio_pu_pin>; + pinctrl-3 = <&P9_22_gpio_pd_pin>; + pinctrl-4 = <&P9_22_spi_sclk_pin>; + pinctrl-5 = <&P9_22_uart_pin>; + pinctrl-6 = <&P9_22_i2c_pin>; + pinctrl-7 = <&P9_22_pwm_pin>; + pinctrl-8 = <&P9_22_pru_uart_pin>; + }; + + /* P9_23 (ZCZ ball V14) */ + P9_23_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P9_23_default_pin>; + pinctrl-1 = <&P9_23_gpio_pin>; + pinctrl-2 = <&P9_23_gpio_pu_pin>; + pinctrl-3 = <&P9_23_gpio_pd_pin>; + pinctrl-4 = <&P9_23_pwm_pin>; + }; + + /* P9_24 (ZCZ ball D15) */ + P9_24_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart", "can", "i2c", "pru_uart", "pruin"; + pinctrl-0 = <&P9_24_default_pin>; + pinctrl-1 = <&P9_24_gpio_pin>; + pinctrl-2 = <&P9_24_gpio_pu_pin>; + pinctrl-3 = <&P9_24_gpio_pd_pin>; + pinctrl-4 = <&P9_24_uart_pin>; + pinctrl-5 = <&P9_24_can_pin>; + pinctrl-6 = <&P9_24_i2c_pin>; + pinctrl-7 = <&P9_24_pru_uart_pin>; + pinctrl-8 = <&P9_24_pruin_pin>; + }; + + /* P9_25 (ZCZ ball A14) audio */ + P9_25_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout", "pruin"; + pinctrl-0 = <&P9_25_default_pin>; + pinctrl-1 = <&P9_25_gpio_pin>; + pinctrl-2 = <&P9_25_gpio_pu_pin>; + pinctrl-3 = <&P9_25_gpio_pd_pin>; + pinctrl-4 = <&P9_25_eqep_pin>; + pinctrl-5 = <&P9_25_pruout_pin>; + pinctrl-6 = <&P9_25_pruin_pin>; + }; + + /* P9_26 (ZCZ ball D16) */ + P9_26_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart", "can", "i2c", "pru_uart", "pruin"; + pinctrl-0 = <&P9_26_default_pin>; + pinctrl-1 = <&P9_26_gpio_pin>; + pinctrl-2 = <&P9_26_gpio_pu_pin>; + pinctrl-3 = <&P9_26_gpio_pd_pin>; + pinctrl-4 = <&P9_26_uart_pin>; + pinctrl-5 = <&P9_26_can_pin>; + pinctrl-6 = <&P9_26_i2c_pin>; + pinctrl-7 = <&P9_26_pru_uart_pin>; + pinctrl-8 = <&P9_26_pruin_pin>; + }; + + /* P9_27 (ZCZ ball C13) */ + P9_27_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout", "pruin"; + pinctrl-0 = <&P9_27_default_pin>; + pinctrl-1 = <&P9_27_gpio_pin>; + pinctrl-2 = <&P9_27_gpio_pu_pin>; + pinctrl-3 = <&P9_27_gpio_pd_pin>; + pinctrl-4 = <&P9_27_eqep_pin>; + pinctrl-5 = <&P9_27_pruout_pin>; + pinctrl-6 = <&P9_27_pruin_pin>; + }; + + /* P9_28 (ZCZ ball C12) audio */ + P9_28_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_cs", "pwm", "pwm2", "pruout", "pruin"; + pinctrl-0 = <&P9_28_default_pin>; + pinctrl-1 = <&P9_28_gpio_pin>; + pinctrl-2 = <&P9_28_gpio_pu_pin>; + pinctrl-3 = <&P9_28_gpio_pd_pin>; + pinctrl-4 = <&P9_28_spi_cs_pin>; + pinctrl-5 = <&P9_28_pwm_pin>; + pinctrl-6 = <&P9_28_pwm2_pin>; + pinctrl-7 = <&P9_28_pruout_pin>; + pinctrl-8 = <&P9_28_pruin_pin>; + }; + + /* P9_29 (ZCZ ball B13) audio */ + P9_29_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P9_29_default_pin>; + pinctrl-1 = <&P9_29_gpio_pin>; + pinctrl-2 = <&P9_29_gpio_pu_pin>; + pinctrl-3 = <&P9_29_gpio_pd_pin>; + pinctrl-4 = <&P9_29_spi_pin>; + pinctrl-5 = <&P9_29_pwm_pin>; + pinctrl-6 = <&P9_29_pruout_pin>; + pinctrl-7 = <&P9_29_pruin_pin>; + }; + + /* P9_30 (ZCZ ball D12) */ + P9_30_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P9_30_default_pin>; + pinctrl-1 = <&P9_30_gpio_pin>; + pinctrl-2 = <&P9_30_gpio_pu_pin>; + pinctrl-3 = <&P9_30_gpio_pd_pin>; + pinctrl-4 = <&P9_30_spi_pin>; + pinctrl-5 = <&P9_30_pwm_pin>; + pinctrl-6 = <&P9_30_pruout_pin>; + pinctrl-7 = <&P9_30_pruin_pin>; + }; + + /* P9_31 (ZCZ ball A13) audio */ + P9_31_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_sclk", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P9_31_default_pin>; + pinctrl-1 = <&P9_31_gpio_pin>; + pinctrl-2 = <&P9_31_gpio_pu_pin>; + pinctrl-3 = <&P9_31_gpio_pd_pin>; + pinctrl-4 = <&P9_31_spi_sclk_pin>; + pinctrl-5 = <&P9_31_pwm_pin>; + pinctrl-6 = <&P9_31_pruout_pin>; + pinctrl-7 = <&P9_31_pruin_pin>; + }; + + /* P9_32 VADC */ + + /* P9_33 (ZCZ ball C8) AIN4 */ + + /* P9_34 AGND */ + + /* P9_35 (ZCZ ball A8) AIN6 */ + + /* P9_36 (ZCZ ball B8) AIN5 */ + + /* P9_37 (ZCZ ball B7) AIN2 */ + + /* P9_38 (ZCZ ball A7) AIN3 */ + + /* P9_39 (ZCZ ball B6) AIN0 */ + + /* P9_40 (ZCZ ball C7) AIN1 */ + + /* P9_41 (ZCZ ball D14) */ + P9_41_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "timer", "pruin"; + pinctrl-0 = <&P9_41_default_pin>; + pinctrl-1 = <&P9_41_gpio_pin>; + pinctrl-2 = <&P9_41_gpio_pu_pin>; + pinctrl-3 = <&P9_41_gpio_pd_pin>; + pinctrl-4 = <&P9_41_timer_pin>; + pinctrl-5 = <&P9_41_pruin_pin>; + }; + + /* P9_41.1 */ + /* P9_91 (ZCZ ball D13) */ + P9_91_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout", "pruin"; + pinctrl-0 = <&P9_91_default_pin>; + pinctrl-1 = <&P9_91_gpio_pin>; + pinctrl-2 = <&P9_91_gpio_pu_pin>; + pinctrl-3 = <&P9_91_gpio_pd_pin>; + pinctrl-4 = <&P9_91_eqep_pin>; + pinctrl-5 = <&P9_91_pruout_pin>; + pinctrl-6 = <&P9_91_pruin_pin>; + }; + + /* P9_42 (ZCZ ball C18) */ + P9_42_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_cs", "spi_sclk", "uart", "pwm", "pru_ecap_pwm"; + pinctrl-0 = <&P9_42_default_pin>; + pinctrl-1 = <&P9_42_gpio_pin>; + pinctrl-2 = <&P9_42_gpio_pu_pin>; + pinctrl-3 = <&P9_42_gpio_pd_pin>; + pinctrl-4 = <&P9_42_spi_cs_pin>; + pinctrl-5 = <&P9_42_spi_sclk_pin>; + pinctrl-6 = <&P9_42_uart_pin>; + pinctrl-7 = <&P9_42_pwm_pin>; + pinctrl-8 = <&P9_42_pru_ecap_pwm_pin>; + }; + + /* P9_42.1 */ + /* P9_92 (ZCZ ball B12) */ + P9_92_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout", "pruin"; + pinctrl-0 = <&P9_92_default_pin>; + pinctrl-1 = <&P9_92_gpio_pin>; + pinctrl-2 = <&P9_92_gpio_pu_pin>; + pinctrl-3 = <&P9_92_gpio_pd_pin>; + pinctrl-4 = <&P9_92_eqep_pin>; + pinctrl-5 = <&P9_92_pruout_pin>; + pinctrl-6 = <&P9_92_pruin_pin>; + }; + + /* P9_43 GND */ + + /* P9_44 GND */ + + /* P9_45 GND */ + + /* P9_46 GND */ + + cape-universal { + compatible = "gpio-of-helper"; + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + + P8_03 { + gpio-name = "P8_03"; + gpio = <&gpio1 6 0>; + input; + dir-changeable; + }; + + P8_04 { + gpio-name = "P8_04"; + gpio = <&gpio1 7 0>; + input; + dir-changeable; + }; + + P8_05 { + gpio-name = "P8_05"; + gpio = <&gpio1 2 0>; + input; + dir-changeable; + }; + + P8_06 { + gpio-name = "P8_06"; + gpio = <&gpio1 3 0>; + input; + dir-changeable; + }; + + P8_07 { + gpio-name = "P8_07"; + gpio = <&gpio2 2 0>; + input; + dir-changeable; + }; + + P8_08 { + gpio-name = "P8_08"; + gpio = <&gpio2 3 0>; + input; + dir-changeable; + }; + + P8_09 { + gpio-name = "P8_09"; + gpio = <&gpio2 5 0>; + input; + dir-changeable; + }; + + P8_10 { + gpio-name = "P8_10"; + gpio = <&gpio2 4 0>; + input; + dir-changeable; + }; + + P8_11 { + gpio-name = "P8_11"; + gpio = <&gpio1 13 0>; + input; + dir-changeable; + }; + + P8_12 { + gpio-name = "P8_12"; + gpio = <&gpio1 12 0>; + input; + dir-changeable; + }; + + P8_13 { + gpio-name = "P8_13"; + gpio = <&gpio0 23 0>; + input; + dir-changeable; + }; + + P8_14 { + gpio-name = "P8_14"; + gpio = <&gpio0 26 0>; + input; + dir-changeable; + }; + + P8_15 { + gpio-name = "P8_15"; + gpio = <&gpio1 15 0>; + input; + dir-changeable; + }; + + P8_16 { + gpio-name = "P8_16"; + gpio = <&gpio1 14 0>; + input; + dir-changeable; + }; + + P8_17 { + gpio-name = "P8_17"; + gpio = <&gpio0 27 0>; + input; + dir-changeable; + }; + + P8_18 { + gpio-name = "P8_18"; + gpio = <&gpio2 1 0>; + input; + dir-changeable; + }; + + P8_19 { + gpio-name = "P8_19"; + gpio = <&gpio0 22 0>; + input; + dir-changeable; + }; + + P8_20 { + gpio-name = "P8_20"; + gpio = <&gpio1 31 0>; + input; + dir-changeable; + }; + + P8_21 { + gpio-name = "P8_21"; + gpio = <&gpio1 30 0>; + input; + dir-changeable; + }; + + P8_22 { + gpio-name = "P8_22"; + gpio = <&gpio1 5 0>; + input; + dir-changeable; + }; + + P8_23 { + gpio-name = "P8_23"; + gpio = <&gpio1 4 0>; + input; + dir-changeable; + }; + + P8_24 { + gpio-name = "P8_24"; + gpio = <&gpio1 1 0>; + input; + dir-changeable; + }; + + P8_25 { + gpio-name = "P8_25"; + gpio = <&gpio1 0 0>; + input; + dir-changeable; + }; + + P8_26 { + gpio-name = "P8_26"; + gpio = <&gpio1 29 0>; + input; + dir-changeable; + }; + + P8_27 { + gpio-name = "P8_27"; + gpio = <&gpio2 22 0>; + input; + dir-changeable; + }; + + P8_28 { + gpio-name = "P8_28"; + gpio = <&gpio2 24 0>; + input; + dir-changeable; + }; + + P8_29 { + gpio-name = "P8_29"; + gpio = <&gpio2 23 0>; + input; + dir-changeable; + }; + + P8_30 { + gpio-name = "P8_30"; + gpio = <&gpio2 25 0>; + input; + dir-changeable; + }; + + P8_31 { + gpio-name = "P8_31"; + gpio = <&gpio0 10 0>; + input; + dir-changeable; + }; + + P8_32 { + gpio-name = "P8_32"; + gpio = <&gpio0 11 0>; + input; + dir-changeable; + }; + + P8_33 { + gpio-name = "P8_33"; + gpio = <&gpio0 9 0>; + input; + dir-changeable; + }; + + P8_34 { + gpio-name = "P8_34"; + gpio = <&gpio2 17 0>; + input; + dir-changeable; + }; + + P8_35 { + gpio-name = "P8_35"; + gpio = <&gpio0 8 0>; + input; + dir-changeable; + }; + + P8_36 { + gpio-name = "P8_36"; + gpio = <&gpio2 16 0>; + input; + dir-changeable; + }; + + P8_37 { + gpio-name = "P8_37"; + gpio = <&gpio2 14 0>; + input; + dir-changeable; + }; + + P8_38 { + gpio-name = "P8_38"; + gpio = <&gpio2 15 0>; + input; + dir-changeable; + }; + + P8_39 { + gpio-name = "P8_39"; + gpio = <&gpio2 12 0>; + input; + dir-changeable; + }; + + P8_40 { + gpio-name = "P8_40"; + gpio = <&gpio2 13 0>; + input; + dir-changeable; + }; + + P8_41 { + gpio-name = "P8_41"; + gpio = <&gpio2 10 0>; + input; + dir-changeable; + }; + + P8_42 { + gpio-name = "P8_42"; + gpio = <&gpio2 11 0>; + input; + dir-changeable; + }; + + P8_43 { + gpio-name = "P8_43"; + gpio = <&gpio2 8 0>; + input; + dir-changeable; + }; + + P8_44 { + gpio-name = "P8_44"; + gpio = <&gpio2 9 0>; + input; + dir-changeable; + }; + + P8_45 { + gpio-name = "P8_45"; + gpio = <&gpio2 6 0>; + input; + dir-changeable; + }; + + P8_46 { + gpio-name = "P8_46"; + gpio = <&gpio2 7 0>; + input; + dir-changeable; + }; + + P9_11 { + gpio-name = "P9_11"; + gpio = <&gpio0 30 0>; + input; + dir-changeable; + }; + + P9_12 { + gpio-name = "P9_12"; + gpio = <&gpio1 28 0>; + input; + dir-changeable; + }; + + P9_13 { + gpio-name = "P9_13"; + gpio = <&gpio0 31 0>; + input; + dir-changeable; + }; + + P9_14 { + gpio-name = "P9_14"; + gpio = <&gpio1 18 0>; + input; + dir-changeable; + }; + + P9_15 { + gpio-name = "P9_15"; + gpio = <&gpio1 16 0>; + input; + dir-changeable; + }; + + P9_16 { + gpio-name = "P9_16"; + gpio = <&gpio1 19 0>; + input; + dir-changeable; + }; + + P9_17 { + gpio-name = "P9_17"; + gpio = <&gpio0 5 0>; + input; + dir-changeable; + }; + + P9_18 { + gpio-name = "P9_18"; + gpio = <&gpio0 4 0>; + input; + dir-changeable; + }; + + P9_19 { + gpio-name = "P9_19"; + gpio = <&gpio0 13 0>; + input; + dir-changeable; + }; + + P9_20 { + gpio-name = "P9_20"; + gpio = <&gpio0 12 0>; + input; + dir-changeable; + }; + + P9_21 { + gpio-name = "P9_21"; + gpio = <&gpio0 3 0>; + input; + dir-changeable; + }; + + P9_22 { + gpio-name = "P9_22"; + gpio = <&gpio0 2 0>; + input; + dir-changeable; + }; + + P9_23 { + gpio-name = "P9_23"; + gpio = <&gpio1 17 0>; + input; + dir-changeable; + }; + + P9_24 { + gpio-name = "P9_24"; + gpio = <&gpio0 15 0>; + input; + dir-changeable; + }; + + P9_25 { + gpio-name = "P9_25"; + gpio = <&gpio3 21 0>; + input; + dir-changeable; + }; + + P9_26 { + gpio-name = "P9_26"; + gpio = <&gpio0 14 0>; + input; + dir-changeable; + }; + + P9_27 { + gpio-name = "P9_27"; + gpio = <&gpio3 19 0>; + input; + dir-changeable; + }; + + P9_28 { + gpio-name = "P9_28"; + gpio = <&gpio3 17 0>; + input; + dir-changeable; + }; + + P9_29 { + gpio-name = "P9_29"; + gpio = <&gpio3 15 0>; + input; + dir-changeable; + }; + + P9_30 { + gpio-name = "P9_30"; + gpio = <&gpio3 16 0>; + input; + dir-changeable; + }; + + P9_31 { + gpio-name = "P9_31"; + gpio = <&gpio3 14 0>; + input; + dir-changeable; + }; + + P9_41 { + gpio-name = "P9_41"; + gpio = <&gpio0 20 0>; + input; + dir-changeable; + }; + + P9_91 { + gpio-name = "P9_91"; + gpio = <&gpio3 20 0>; + input; + dir-changeable; + }; + + P9_42 { + gpio-name = "P9_42"; + gpio = <&gpio0 7 0>; + input; + dir-changeable; + }; + + P9_92 { + gpio-name = "P9_92"; + gpio = <&gpio3 18 0>; + input; + dir-changeable; + }; + }; +}; diff --git a/arch/arm/boot/dts/am335x-bone-common.dtsi b/arch/arm/boot/dts/am335x-bone-common.dtsi index b571227cec63b..f1cf99566bf5e 100644 --- a/arch/arm/boot/dts/am335x-bone-common.dtsi +++ b/arch/arm/boot/dts/am335x-bone-common.dtsi @@ -4,10 +4,10 @@ */ / { - aliases { - ethernet0 = &cpsw_port1; - ethernet1 = &cpsw_port2; - }; + //aliases { + // ethernet0 = &cpsw_port1; + // ethernet1 = &cpsw_port2; + //}; cpus { cpu@0 { @@ -31,14 +31,14 @@ compatible = "gpio-leds"; led2 { - label = "beaglebone:green:heartbeat"; + label = "beaglebone:green:usr0"; gpios = <&gpio1 21 GPIO_ACTIVE_HIGH>; linux,default-trigger = "heartbeat"; default-state = "off"; }; led3 { - label = "beaglebone:green:mmc0"; + label = "beaglebone:green:usr1"; gpios = <&gpio1 22 GPIO_ACTIVE_HIGH>; linux,default-trigger = "mmc0"; default-state = "off"; @@ -68,9 +68,6 @@ }; &am33xx_pinmux { - pinctrl-names = "default"; - pinctrl-0 = <&clkout2_pin>; - user_leds_s0: user_leds_s0 { pinctrl-single,pins = < AM33XX_PADCONF(AM335X_PIN_GPMC_A5, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gpmc_a5.gpio1_21 */ @@ -101,12 +98,6 @@ >; }; - clkout2_pin: pinmux_clkout2_pin { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR1, PIN_OUTPUT_PULLDOWN, MUX_MODE3) /* xdma_event_intr1.clkout2 */ - >; - }; - cpsw_default: cpsw_default { pinctrl-single,pins = < /* Slave 1 */ @@ -194,6 +185,7 @@ pinctrl-0 = <&uart0_pins>; status = "okay"; + symlink = "bone/uart/0"; }; &usb0 { @@ -212,6 +204,7 @@ status = "okay"; clock-frequency = <400000>; + symlink = "bone/i2c/0"; tps: tps@24 { reg = <0x24>; @@ -235,6 +228,7 @@ status = "okay"; clock-frequency = <100000>; + symlink = "bone/i2c/2"; cape_eeprom0: cape_eeprom0@54 { compatible = "atmel,24c256"; @@ -358,27 +352,24 @@ }; }; -&cpsw_port1 { +&cpsw_emac0 { phy-handle = <ðphy0>; phy-mode = "mii"; - ti,dual-emac-pvid = <1>; }; -&cpsw_port2 { - status = "disabled"; -}; - -&mac_sw { +&mac { + slaves = <1>; pinctrl-names = "default", "sleep"; pinctrl-0 = <&cpsw_default>; pinctrl-1 = <&cpsw_sleep>; status = "okay"; }; -&davinci_mdio_sw { +&davinci_mdio { pinctrl-names = "default", "sleep"; pinctrl-0 = <&davinci_mdio_default>; pinctrl-1 = <&davinci_mdio_sleep>; + status = "okay"; ethphy0: ethernet-phy@0 { reg = <0>; @@ -414,3 +405,12 @@ &wkup_m3_ipc { ti,scale-data-fw = "am335x-bone-scale-data.bin"; }; + +&tscadc { + adc { + ti,adc-channels = <0 1 2 3 4 5 6 7>; + ti,chan-step-avg = <16 16 16 16 16 16 16 16>; + ti,chan-step-opendelay = <0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98>; + ti,chan-step-sampledelay = <0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0>; + }; +}; diff --git a/arch/arm/boot/dts/am335x-bone-jtag.dtsi b/arch/arm/boot/dts/am335x-bone-jtag.dtsi new file mode 100644 index 0000000000000..19c8bcf4b4850 --- /dev/null +++ b/arch/arm/boot/dts/am335x-bone-jtag.dtsi @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +&am33xx_pinmux { + pinctrl-names = "default"; + pinctrl-0 = <&clkout2_pin>; + + clkout2_pin: pinmux_clkout2_pin { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR1, PIN_OUTPUT_PULLDOWN, MUX_MODE3) /* xdma_event_intr1.clkout2 */ + >; + }; +}; diff --git a/arch/arm/boot/dts/am335x-bone-uboot-univ.dts b/arch/arm/boot/dts/am335x-bone-uboot-univ.dts new file mode 100644 index 0000000000000..ac9f257025c81 --- /dev/null +++ b/arch/arm/boot/dts/am335x-bone-uboot-univ.dts @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am335x-bone-common-univ.dtsi" + +/ { + model = "TI AM335x BeagleBone"; + compatible = "ti,am335x-bone", "ti,am33xx"; + + chosen { + base_dtb = "am335x-bone-uboot-univ.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&ldo3_reg>; +}; diff --git a/arch/arm/boot/dts/am335x-bone.dts b/arch/arm/boot/dts/am335x-bone.dts index b5d85ef51a021..ea138cb34091e 100644 --- a/arch/arm/boot/dts/am335x-bone.dts +++ b/arch/arm/boot/dts/am335x-bone.dts @@ -10,6 +10,11 @@ / { model = "TI AM335x BeagleBone"; compatible = "ti,am335x-bone", "ti,am33xx"; + + chosen { + base_dtb = "am335x-bone.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; }; &ldo3_reg { diff --git a/arch/arm/boot/dts/am335x-boneblack-common.dtsi b/arch/arm/boot/dts/am335x-boneblack-common.dtsi index 64c3e9269f40c..10494c4431b92 100644 --- a/arch/arm/boot/dts/am335x-boneblack-common.dtsi +++ b/arch/arm/boot/dts/am335x-boneblack-common.dtsi @@ -3,9 +3,6 @@ * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ */ -#include -#include - &ldo3_reg { regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; @@ -25,145 +22,13 @@ non-removable; }; -&am33xx_pinmux { - nxp_hdmi_bonelt_pins: nxp_hdmi_bonelt_pins { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_OUTPUT_PULLDOWN, MUX_MODE3) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA0, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA1, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA2, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA3, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA4, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA5, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA6, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA7, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA8, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA9, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA10, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA11, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA12, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA13, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA14, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA15, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_VSYNC, PIN_OUTPUT_PULLDOWN, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_HSYNC, PIN_OUTPUT_PULLDOWN, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_PCLK, PIN_OUTPUT_PULLDOWN, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_AC_BIAS_EN, PIN_OUTPUT_PULLDOWN, MUX_MODE0) - >; - }; - - nxp_hdmi_bonelt_off_pins: nxp_hdmi_bonelt_off_pins { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_OUTPUT_PULLDOWN, MUX_MODE3) - >; - }; - - mcasp0_pins: mcasp0_pins { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKX, PIN_INPUT_PULLUP, MUX_MODE0) /* mcasp0_ahcklx.mcasp0_ahclkx */ - AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKR, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mcasp0_ahclkr.mcasp0_axr2*/ - AM33XX_PADCONF(AM335X_PIN_MCASP0_FSX, PIN_OUTPUT_PULLUP, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_MCASP0_ACLKX, PIN_OUTPUT_PULLDOWN, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_GPMC_A11, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gpmc_a11.GPIO1_27 */ - >; - }; -}; - -&lcdc { - status = "okay"; - - /* If you want to get 24 bit RGB and 16 BGR mode instead of - * current 16 bit RGB and 24 BGR modes, set the propety - * below to "crossed" and uncomment the video-ports -property - * in tda19988 node. - */ - blue-and-red-wiring = "straight"; - - port { - lcdc_0: endpoint@0 { - remote-endpoint = <&hdmi_0>; - }; - }; -}; - -&i2c0 { - tda19988: tda19988@70 { - compatible = "nxp,tda998x"; - reg = <0x70>; - nxp,calib-gpios = <&gpio1 25 0>; - interrupts-extended = <&gpio1 25 IRQ_TYPE_LEVEL_LOW>; - - pinctrl-names = "default", "off"; - pinctrl-0 = <&nxp_hdmi_bonelt_pins>; - pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>; - - /* Convert 24bit BGR to RGB, e.g. cross red and blue wiring */ - /* video-ports = <0x234501>; */ - - #sound-dai-cells = <0>; - audio-ports = < TDA998x_I2S 0x03>; - - ports { - port@0 { - hdmi_0: endpoint@0 { - remote-endpoint = <&lcdc_0>; - }; - }; - }; - }; -}; - &rtc { system-power-controller; }; -&mcasp0 { - #sound-dai-cells = <0>; - pinctrl-names = "default"; - pinctrl-0 = <&mcasp0_pins>; - status = "okay"; - op-mode = <0>; /* MCASP_IIS_MODE */ - tdm-slots = <2>; - serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */ - 0 0 1 0 - >; - tx-num-evt = <32>; - rx-num-evt = <32>; -}; - / { memory@80000000 { device_type = "memory"; reg = <0x80000000 0x20000000>; /* 512 MB */ }; - - clk_mcasp0_fixed: clk_mcasp0_fixed { - #clock-cells = <0>; - compatible = "fixed-clock"; - clock-frequency = <24576000>; - }; - - clk_mcasp0: clk_mcasp0 { - #clock-cells = <0>; - compatible = "gpio-gate-clock"; - clocks = <&clk_mcasp0_fixed>; - enable-gpios = <&gpio1 27 0>; /* BeagleBone Black Clk enable on GPIO1_27 */ - }; - - sound { - compatible = "simple-audio-card"; - simple-audio-card,name = "TI BeagleBone Black"; - simple-audio-card,format = "i2s"; - simple-audio-card,bitclock-master = <&dailink0_master>; - simple-audio-card,frame-master = <&dailink0_master>; - - dailink0_master: simple-audio-card,cpu { - sound-dai = <&mcasp0>; - clocks = <&clk_mcasp0>; - }; - - simple-audio-card,codec { - sound-dai = <&tda19988>; - }; - }; }; diff --git a/arch/arm/boot/dts/am335x-boneblack-hdmi.dtsi b/arch/arm/boot/dts/am335x-boneblack-hdmi.dtsi new file mode 100644 index 0000000000000..7cfddada93486 --- /dev/null +++ b/arch/arm/boot/dts/am335x-boneblack-hdmi.dtsi @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#include +#include + +&am33xx_pinmux { + nxp_hdmi_bonelt_pins: nxp_hdmi_bonelt_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_OUTPUT_PULLDOWN, MUX_MODE3) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA0, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA1, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA2, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA3, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA4, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA5, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA6, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA7, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA8, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA9, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA10, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA11, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA12, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA13, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA14, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA15, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_VSYNC, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_HSYNC, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_PCLK, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_AC_BIAS_EN, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + >; + }; + + nxp_hdmi_bonelt_off_pins: nxp_hdmi_bonelt_off_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_OUTPUT_PULLDOWN, MUX_MODE3) + >; + }; + + mcasp0_pins: mcasp0_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKX, PIN_INPUT_PULLUP, MUX_MODE0) /* mcasp0_ahcklx.mcasp0_ahclkx */ + AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKR, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mcasp0_ahclkr.mcasp0_axr2*/ + AM33XX_PADCONF(AM335X_PIN_MCASP0_FSX, PIN_OUTPUT_PULLUP, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_MCASP0_ACLKX, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_GPMC_A11, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gpmc_a11.GPIO1_27 */ + >; + }; +}; + +&lcdc { + status = "okay"; + + /* If you want to get 24 bit RGB and 16 BGR mode instead of + * current 16 bit RGB and 24 BGR modes, set the propety + * below to "crossed" and uncomment the video-ports -property + * in tda19988 node. + */ + blue-and-red-wiring = "straight"; + + port { + lcdc_0: endpoint@0 { + remote-endpoint = <&hdmi_0>; + }; + }; +}; + +&i2c0 { + tda19988: tda19988@70 { + compatible = "nxp,tda998x"; + reg = <0x70>; + nxp,calib-gpios = <&gpio1 25 0>; + interrupts-extended = <&gpio1 25 IRQ_TYPE_LEVEL_LOW>; + + pinctrl-names = "default", "off"; + pinctrl-0 = <&nxp_hdmi_bonelt_pins>; + pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>; + + /* Convert 24bit BGR to RGB, e.g. cross red and blue wiring */ + /* video-ports = <0x234501>; */ + + #sound-dai-cells = <0>; + audio-ports = < TDA998x_I2S 0x03>; + + ports { + port@0 { + hdmi_0: endpoint@0 { + remote-endpoint = <&lcdc_0>; + }; + }; + }; + }; +}; + +&mcasp0 { + #sound-dai-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&mcasp0_pins>; + status = "okay"; + op-mode = <0>; /* MCASP_IIS_MODE */ + tdm-slots = <2>; + serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */ + 0 0 1 0 + >; + tx-num-evt = <32>; + rx-num-evt = <32>; +}; + +/ { + clk_mcasp0_fixed: clk_mcasp0_fixed { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <24576000>; + }; + + clk_mcasp0: clk_mcasp0 { + #clock-cells = <0>; + compatible = "gpio-gate-clock"; + clocks = <&clk_mcasp0_fixed>; + enable-gpios = <&gpio1 27 0>; /* BeagleBone Black Clk enable on GPIO1_27 */ + }; + + sound { + compatible = "simple-audio-card"; + simple-audio-card,name = "TI BeagleBone Black"; + simple-audio-card,format = "i2s"; + simple-audio-card,bitclock-master = <&dailink0_master>; + simple-audio-card,frame-master = <&dailink0_master>; + + dailink0_master: simple-audio-card,cpu { + sound-dai = <&mcasp0>; + clocks = <&clk_mcasp0>; + }; + + simple-audio-card,codec { + sound-dai = <&tda19988>; + }; + }; +}; diff --git a/arch/arm/boot/dts/am335x-boneblack-uboot-univ.dts b/arch/arm/boot/dts/am335x-boneblack-uboot-univ.dts new file mode 100644 index 0000000000000..28156a302ff54 --- /dev/null +++ b/arch/arm/boot/dts/am335x-boneblack-uboot-univ.dts @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am335x-bone-common-univ.dtsi" +#include "am335x-bbb-bone-buses.dtsi" + +/ { + model = "TI AM335x BeagleBone Black"; + compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + chosen { + base_dtb = "am335x-boneblack-uboot-univ.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; +}; + +&cpu0_opp_table { + /* + * All PG 2.0 silicon may not support 1GHz but some of the early + * BeagleBone Blacks have PG 2.0 silicon which is guaranteed + * to support 1GHz OPP so enable it for PG 2.0 on this board. + */ + oppnitro-1000000000 { + opp-supported-hw = <0x06 0x0100>; + }; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; + +&gpio0 { + gpio-line-names = + "[mdio_data]", + "[mdio_clk]", + "P9_22 [spi0_sclk]", + "P9_21 [spi0_d0]", + "P9_18 [spi0_d1]", + "P9_17 [spi0_cs0]", + "[mmc0_cd]", + "P8_42A [ecappwm0]", + "P8_35 [lcd d12]", + "P8_33 [lcd d13]", + "P8_31 [lcd d14]", + "P8_32 [lcd d15]", + "P9_20 [i2c2_sda]", + "P9_19 [i2c2_scl]", + "P9_26 [uart1_rxd]", + "P9_24 [uart1_txd]", + "[rmii1_txd3]", + "[rmii1_txd2]", + "[usb0_drvvbus]", + "[hdmi cec]", + "P9_41B", + "[rmii1_txd1]", + "P8_19 [ehrpwm2a]", + "P8_13 [ehrpwm2b]", + "NC", + "NC", + "P8_14", + "P8_17", + "[rmii1_txd0]", + "[rmii1_refclk]", + "P9_11 [uart4_rxd]", + "P9_13 [uart4_txd]"; +}; + +&gpio1 { + gpio-line-names = + "P8_25 [mmc1_dat0]", + "[mmc1_dat1]", + "P8_5 [mmc1_dat2]", + "P8_6 [mmc1_dat3]", + "P8_23 [mmc1_dat4]", + "P8_22 [mmc1_dat5]", + "P8_3 [mmc1_dat6]", + "P8_4 [mmc1_dat7]", + "NC", + "NC", + "NC", + "NC", + "P8_12", + "P8_11", + "P8_16", + "P8_15", + "P9_15A", + "P9_23", + "P9_14 [ehrpwm1a]", + "P9_16 [ehrpwm1b]", + "[emmc rst]", + "[usr0 led]", + "[usr1 led]", + "[usr2 led]", + "[usr3 led]", + "[hdmi irq]", + "[usb vbus oc]", + "[hdmi audio]", + "P9_12", + "P8_26", + "P8_21 [emmc]", + "P8_20 [emmc]"; +}; + +&gpio2 { + gpio-line-names = + "P9_15B", + "P8_18", + "P8_7", + "P8_8", + "P8_10", + "P8_9", + "P8_45 [hdmi]", + "P8_46 [hdmi]", + "P8_43 [hdmi]", + "P8_44 [hdmi]", + "P8_41 [hdmi]", + "P8_42 [hdmi]", + "P8_39 [hdmi]", + "P8_40 [hdmi]", + "P8_37 [hdmi]", + "P8_38 [hdmi]", + "P8_36 [hdmi]", + "P8_34 [hdmi]", + "[rmii1_rxd3]", + "[rmii1_rxd2]", + "[rmii1_rxd1]", + "[rmii1_rxd0]", + "P8_27 [hdmi]", + "P8_29 [hdmi]", + "P8_28 [hdmi]", + "P8_30 [hdmi]", + "[mmc0_dat3]", + "[mmc0_dat2]", + "[mmc0_dat1]", + "[mmc0_dat0]", + "[mmc0_clk]", + "[mmc0_cmd]"; +}; + +&gpio3 { + gpio-line-names = + "[mii col]", + "[mii crs]", + "[mii rx err]", + "[mii tx en]", + "[mii rx dv]", + "[i2c0 sda]", + "[i2c0 scl]", + "[jtag emu0]", + "[jtag emu1]", + "[mii tx clk]", + "[mii rx clk]", + "NC", + "NC", + "[usb vbus en]", + "P9_31 [spi1_sclk]", + "P9_29 [spi1_d0]", + "P9_30 [spi1_d1]", + "P9_28 [spi1_cs0]", + "P9_42B [ecappwm0]", + "P9_27", + "P9_41A", + "P9_25", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC"; +}; diff --git a/arch/arm/boot/dts/am335x-boneblack-uboot.dts b/arch/arm/boot/dts/am335x-boneblack-uboot.dts new file mode 100644 index 0000000000000..4226cc5005695 --- /dev/null +++ b/arch/arm/boot/dts/am335x-boneblack-uboot.dts @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am335x-bbb-bone-buses.dtsi" + +/ { + model = "TI AM335x BeagleBone Black"; + compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + chosen { + base_dtb = "am335x-boneblack-uboot.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0x80000000 0x20000000>; /* 512 MB */ + }; +}; + +&cpu0_opp_table { + /* + * All PG 2.0 silicon may not support 1GHz but some of the early + * BeagleBone Blacks have PG 2.0 silicon which is guaranteed + * to support 1GHz OPP so enable it for PG 2.0 on this board. + */ + oppnitro-1000000000 { + opp-supported-hw = <0x06 0x0100>; + }; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; + +&rtc { + system-power-controller; +}; + +&gpio0 { + gpio-line-names = + "[mdio_data]", + "[mdio_clk]", + "P9_22 [spi0_sclk]", + "P9_21 [spi0_d0]", + "P9_18 [spi0_d1]", + "P9_17 [spi0_cs0]", + "[mmc0_cd]", + "P8_42A [ecappwm0]", + "P8_35 [lcd d12]", + "P8_33 [lcd d13]", + "P8_31 [lcd d14]", + "P8_32 [lcd d15]", + "P9_20 [i2c2_sda]", + "P9_19 [i2c2_scl]", + "P9_26 [uart1_rxd]", + "P9_24 [uart1_txd]", + "[rmii1_txd3]", + "[rmii1_txd2]", + "[usb0_drvvbus]", + "[hdmi cec]", + "P9_41B", + "[rmii1_txd1]", + "P8_19 [ehrpwm2a]", + "P8_13 [ehrpwm2b]", + "NC", + "NC", + "P8_14", + "P8_17", + "[rmii1_txd0]", + "[rmii1_refclk]", + "P9_11 [uart4_rxd]", + "P9_13 [uart4_txd]"; +}; + +&gpio1 { + gpio-line-names = + "P8_25 [mmc1_dat0]", + "[mmc1_dat1]", + "P8_5 [mmc1_dat2]", + "P8_6 [mmc1_dat3]", + "P8_23 [mmc1_dat4]", + "P8_22 [mmc1_dat5]", + "P8_3 [mmc1_dat6]", + "P8_4 [mmc1_dat7]", + "NC", + "NC", + "NC", + "NC", + "P8_12", + "P8_11", + "P8_16", + "P8_15", + "P9_15A", + "P9_23", + "P9_14 [ehrpwm1a]", + "P9_16 [ehrpwm1b]", + "[emmc rst]", + "[usr0 led]", + "[usr1 led]", + "[usr2 led]", + "[usr3 led]", + "[hdmi irq]", + "[usb vbus oc]", + "[hdmi audio]", + "P9_12", + "P8_26", + "P8_21 [emmc]", + "P8_20 [emmc]"; +}; + +&gpio2 { + gpio-line-names = + "P9_15B", + "P8_18", + "P8_7", + "P8_8", + "P8_10", + "P8_9", + "P8_45 [hdmi]", + "P8_46 [hdmi]", + "P8_43 [hdmi]", + "P8_44 [hdmi]", + "P8_41 [hdmi]", + "P8_42 [hdmi]", + "P8_39 [hdmi]", + "P8_40 [hdmi]", + "P8_37 [hdmi]", + "P8_38 [hdmi]", + "P8_36 [hdmi]", + "P8_34 [hdmi]", + "[rmii1_rxd3]", + "[rmii1_rxd2]", + "[rmii1_rxd1]", + "[rmii1_rxd0]", + "P8_27 [hdmi]", + "P8_29 [hdmi]", + "P8_28 [hdmi]", + "P8_30 [hdmi]", + "[mmc0_dat3]", + "[mmc0_dat2]", + "[mmc0_dat1]", + "[mmc0_dat0]", + "[mmc0_clk]", + "[mmc0_cmd]"; +}; + +&gpio3 { + gpio-line-names = + "[mii col]", + "[mii crs]", + "[mii rx err]", + "[mii tx en]", + "[mii rx dv]", + "[i2c0 sda]", + "[i2c0 scl]", + "[jtag emu0]", + "[jtag emu1]", + "[mii tx clk]", + "[mii rx clk]", + "NC", + "NC", + "[usb vbus en]", + "P9_31 [spi1_sclk]", + "P9_29 [spi1_d0]", + "P9_30 [spi1_d1]", + "P9_28 [spi1_cs0]", + "P9_42B [ecappwm0]", + "P9_27", + "P9_41A", + "P9_25", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC"; +}; diff --git a/arch/arm/boot/dts/am335x-boneblack-wireless.dts b/arch/arm/boot/dts/am335x-boneblack-wireless.dts index c6ea78b1ac242..c14391f3e820f 100644 --- a/arch/arm/boot/dts/am335x-boneblack-wireless.dts +++ b/arch/arm/boot/dts/am335x-boneblack-wireless.dts @@ -7,12 +7,18 @@ #include "am33xx.dtsi" #include "am335x-bone-common.dtsi" #include "am335x-boneblack-common.dtsi" +#include "am335x-boneblack-hdmi.dtsi" #include / { model = "TI AM335x BeagleBone Black Wireless"; compatible = "ti,am335x-bone-black-wireless", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + chosen { + base_dtb = "am335x-boneblack-wireless.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; + wlan_en_reg: fixedregulator@2 { compatible = "regulator-fixed"; regulator-name = "wlan-en-regulator"; @@ -62,7 +68,7 @@ }; }; -&mac_sw { +&mac { status = "disabled"; }; @@ -101,7 +107,7 @@ }; &gpio3 { - ls_buf_en { + ls-buf-en-hog { gpio-hog; gpios = <10 GPIO_ACTIVE_HIGH>; output-high; diff --git a/arch/arm/boot/dts/am335x-boneblack.dts b/arch/arm/boot/dts/am335x-boneblack.dts index b4feb85e171ad..2788ab0427279 100644 --- a/arch/arm/boot/dts/am335x-boneblack.dts +++ b/arch/arm/boot/dts/am335x-boneblack.dts @@ -7,10 +7,16 @@ #include "am33xx.dtsi" #include "am335x-bone-common.dtsi" #include "am335x-boneblack-common.dtsi" +#include "am335x-boneblack-hdmi.dtsi" / { model = "TI AM335x BeagleBone Black"; compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + chosen { + base_dtb = "am335x-boneblack.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; }; &cpu0_opp_table { @@ -26,54 +32,54 @@ &gpio0 { gpio-line-names = - "[ethernet]", - "[ethernet]", + "[mdio_data]", + "[mdio_clk]", "P9_22 [spi0_sclk]", "P9_21 [spi0_d0]", "P9_18 [spi0_d1]", "P9_17 [spi0_cs0]", - "[sd card]", - "P9_42A [ecappwm0]", - "P8_35 [hdmi]", - "P8_33 [hdmi]", - "P8_31 [hdmi]", - "P8_32 [hdmi]", + "[mmc0_cd]", + "P8_42A [ecappwm0]", + "P8_35 [lcd d12]", + "P8_33 [lcd d13]", + "P8_31 [lcd d14]", + "P8_32 [lcd d15]", "P9_20 [i2c2_sda]", "P9_19 [i2c2_scl]", "P9_26 [uart1_rxd]", "P9_24 [uart1_txd]", - "[ethernet]", - "[ethernet]", - "[usb]", - "[hdmi]", + "[rmii1_txd3]", + "[rmii1_txd2]", + "[usb0_drvvbus]", + "[hdmi cec]", "P9_41B", - "[ethernet]", + "[rmii1_txd1]", "P8_19 [ehrpwm2a]", "P8_13 [ehrpwm2b]", - "[NC]", - "[NC]", + "NC", + "NC", "P8_14", "P8_17", - "[ethernet]", - "[ethernet]", + "[rmii1_txd0]", + "[rmii1_refclk]", "P9_11 [uart4_rxd]", "P9_13 [uart4_txd]"; }; &gpio1 { gpio-line-names = - "P8_25 [emmc]", - "[emmc]", - "P8_5 [emmc]", - "P8_6 [emmc]", - "P8_23 [emmc]", - "P8_22 [emmc]", - "P8_3 [emmc]", - "P8_4 [emmc]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", + "P8_25 [mmc1_dat0]", + "[mmc1_dat1]", + "P8_5 [mmc1_dat2]", + "P8_6 [mmc1_dat3]", + "P8_23 [mmc1_dat4]", + "P8_22 [mmc1_dat5]", + "P8_3 [mmc1_dat6]", + "P8_4 [mmc1_dat7]", + "NC", + "NC", + "NC", + "NC", "P8_12", "P8_11", "P8_16", @@ -82,13 +88,13 @@ "P9_23", "P9_14 [ehrpwm1a]", "P9_16 [ehrpwm1b]", - "[emmc]", + "[emmc rst]", "[usr0 led]", "[usr1 led]", "[usr2 led]", "[usr3 led]", - "[hdmi]", - "[usb]", + "[hdmi irq]", + "[usb vbus oc]", "[hdmi audio]", "P9_12", "P8_26", @@ -116,38 +122,38 @@ "P8_38 [hdmi]", "P8_36 [hdmi]", "P8_34 [hdmi]", - "[ethernet]", - "[ethernet]", - "[ethernet]", - "[ethernet]", + "[rmii1_rxd3]", + "[rmii1_rxd2]", + "[rmii1_rxd1]", + "[rmii1_rxd0]", "P8_27 [hdmi]", "P8_29 [hdmi]", "P8_28 [hdmi]", "P8_30 [hdmi]", - "[emmc]", - "[emmc]", - "[emmc]", - "[emmc]", - "[emmc]", - "[emmc]"; + "[mmc0_dat3]", + "[mmc0_dat2]", + "[mmc0_dat1]", + "[mmc0_dat0]", + "[mmc0_clk]", + "[mmc0_cmd]"; }; &gpio3 { gpio-line-names = - "[ethernet]", - "[ethernet]", - "[ethernet]", - "[ethernet]", - "[ethernet]", - "[i2c0]", - "[i2c0]", - "[emu]", - "[emu]", - "[ethernet]", - "[ethernet]", - "[NC]", - "[NC]", - "[usb]", + "[mii col]", + "[mii crs]", + "[mii rx err]", + "[mii tx en]", + "[mii rx dv]", + "[i2c0 sda]", + "[i2c0 scl]", + "[jtag emu0]", + "[jtag emu1]", + "[mii tx clk]", + "[mii rx clk]", + "NC", + "NC", + "[usb vbus en]", "P9_31 [spi1_sclk]", "P9_29 [spi1_d0]", "P9_30 [spi1_d1]", @@ -156,14 +162,14 @@ "P9_27", "P9_41A", "P9_25", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]"; + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC"; }; diff --git a/arch/arm/boot/dts/am335x-boneblue.dts b/arch/arm/boot/dts/am335x-boneblue.dts index 69acaf4ea0f35..ed3342145c037 100644 --- a/arch/arm/boot/dts/am335x-boneblue.dts +++ b/arch/arm/boot/dts/am335x-boneblue.dts @@ -14,6 +14,8 @@ chosen { stdout-path = &uart0; + base_dtb = "am335x-boneblue.dts"; + base_dtb_timestamp = __TIMESTAMP__; }; leds { @@ -341,7 +343,7 @@ #address-cells = <1>; #size-cells = <0>; ax8975@c { - compatible = "ak,ak8975"; + compatible = "asahi-kasei,ak8975"; reg = <0x0c>; }; }; @@ -435,12 +437,153 @@ status = "okay"; }; +&gpio0 { + gpio-line-names = + "UART3_CTS", /* M17 */ + "UART3_RTS", /* M18 */ + "UART2_RX", /* A17 */ + "UART2_TX", /* B17 */ + "I2C1_SDA", /* B16 */ + "I2C1_SCL", /* A16 */ + "MMC0_CD", /* C15 */ + "SPI1_SS2", /* C18 */ + "EQEP_1A", /* V2 */ + "EQEP_1B", /* V3 */ + "MDIR_2B", /* V4 */ + "BATT_LED_2", /* T5 */ + "I2C2_SDA", /* D18 */ + "I2C2_SCL", /* D17 */ + "UART1_RX", /* D16 */ + "UART1_TX", /* D15 */ + "MMC2_DAT1", /* J18 */ + "MMC2_DAT2", /* K15 */ + "NC", /* F16 */ + "WIFI_LED", /* A15 */ + "MOT_STBY", /* D14 */ + "WLAN_IRQ", /* K16 */ + "PWM_2A", /* U10 */ + "PWM_2B", /* T10 */ + "", + "", + "BATT_LED_4", /* T11 */ + "BATT_LED_1", /* U12 */ + "BT_EN", /* K17 */ + "SPI1_SS1", /* H18 */ + "UART4_RX", /* T17 */ + "MDIR_1B"; /* U17 */ +}; + +&gpio1 { + gpio-line-names = + "MMC1_DAT0", /* U7 */ + "MMC1_DAT1", /* V7 */ + "MMC1_DAT2", /* R8 */ + "MMC1_DAT3", /* T8 */ + "MMC1_DAT4", /* U8 */ + "MMC1_DAT5", /* V8 */ + "MMC1_DAT6", /* R9 */ + "MMC1_DAT7", /* T9 */ + "DCAN1_TX", /* E18 */ + "DCAN1_RX", /* E17 */ + "UART0_RX", /* E15 */ + "UART0_TX", /* E16 */ + "EQEP_2A", /* T12 */ + "EQEP_2B", /* R12 */ + "PRU_E_A", /* V13 */ + "PRU_E_B", /* U13 */ + "MDIR_2A", /* R13 */ + "GPIO1_17", /* V14 */ + "PWM_1A", /* U14 */ + "PWM_1B", /* T14 */ + "EMMC_RST", /* R14 */ + "USR_LED_0", /* V15 */ + "USR_LED_1", /* U15 */ + "USR_LED_2", /* T15 */ + "USR_LED_3", /* V16 */ + "GPIO1_25", /* U16 */ + "MCASP0_AXR0", /* T16 */ + "MCASP0_AXR1", /* V17 */ + "MCASP0_ACLKR", /* U18 */ + "BATT_LED_3", /* V6 */ + "MMC1_CLK", /* U9 */ + "MMC1_CMD"; /* V9 */ +}; + +&gpio2 { + gpio-line-names = + "MDIR_1A", /* T13 */ + "MCASP0_FSR", /* V12 */ + "LED_RED", /* R7 */ + "LED_GREEN", /* T7 */ + "MODE_BTN", /* U6 */ + "PAUSE_BTN", /* T6 */ + "MDIR_4A", /* R1 */ + "MDIR_4B", /* R2 */ + "MDIR_3B", /* R3 */ + "MDIR_3A", /* R4 */ + "SVO7", /* T1 */ + "SVO8", /* T2 */ + "SVO5", /* T3 */ + "SVO6", /* T4 */ + "UART5_TX", /* U1 */ + "UART5_RX", /* U2 */ + "SERVO_EN", /* U3 */ + "NC", /* U4 */ + "UART3_RX", /* L17 */ + "UART3_TX", /* L16 */ + "MMC2_CLK", /* L15 */ + "DCAN1_SILENT", /* M16 */ + "SVO1", /* U5 */ + "SVO3", /* R5 */ + "SVO2", /* V5 */ + "SVO4", /* R6 */ + "MMC0_DAT3", /* F17 */ + "MMC0_DAT2", /* F18 */ + "MMC0_DAT1", /* G15 */ + "MMC0_DAT0", /* G16 */ + "MMC0_CLK", /* G17 */ + "MMC0_CMD"; /* G18 */ +}; + &gpio3 { - ls_buf_en { + gpio-line-names = + "MMC2_DAT3", /* H16 */ + "GPIO3_1", /* H17 */ + "GPIO3_2", /* J15 */ + "MMC2_CMD", /* J16 */ + "MMC2_DAT0", /* J17 */ + "I2C0_SDA", /* C17 */ + "I2C0_SCL", /* C16 */ + "EMU1", /* C14 */ + "EMU0", /* B14 */ + "WL_EN", /* K18 */ + "WL_BT_OE", /* L18 */ + "", + "", + "NC", /* F15 */ + "SPI1_SCK", /* A13 */ + "SPI1_MISO", /* B13 */ + "SPI1_MOSI", /* D12 */ + "GPIO3_17", /* C12 */ + "EQEP_0A", /* B12 */ + "EQEP_0B", /* C13 */ + "GPIO3_20", /* D13 */ + "IMU_INT", /* A14 */ + "", + "", + "", + "", + "", + "", + "", + "", + "", + ""; + + ls-buf-en-hog { gpio-hog; gpios = <10 GPIO_ACTIVE_HIGH>; output-high; - line-name = "LS_BUF_EN"; }; }; diff --git a/arch/arm/boot/dts/am335x-bonegreen-common.dtsi b/arch/arm/boot/dts/am335x-bonegreen-common.dtsi index 9f7fb63744d01..4c87de57d1a1f 100644 --- a/arch/arm/boot/dts/am335x-bonegreen-common.dtsi +++ b/arch/arm/boot/dts/am335x-bonegreen-common.dtsi @@ -34,6 +34,7 @@ pinctrl-names = "default"; pinctrl-0 = <&uart2_pins>; status = "okay"; + symlink = "bone/uart/2"; }; &rtc { diff --git a/arch/arm/boot/dts/am335x-bonegreen-gateway.dts b/arch/arm/boot/dts/am335x-bonegreen-gateway.dts new file mode 100644 index 0000000000000..ee51c1a7d7d2f --- /dev/null +++ b/arch/arm/boot/dts/am335x-bonegreen-gateway.dts @@ -0,0 +1,260 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am335x-bonegreen-common.dtsi" +#include + +/ { + model = "SeeedStudio BeagleBone Green Gateway"; + compatible = "ti,am335x-bone-green-gateway", "ti,am335x-bone-green", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + aliases { + rtc0 = &extrtc; + rtc1 = &rtc; + }; + + chosen { + base_dtb = "am335x-bonegreen-gateway.dts"; + base_dtb_timestamp = __TIMESTAMP__; + wl1835_bt = "S3-texas-300000"; + }; + + wlan_en_reg: fixedregulator@2 { + compatible = "regulator-fixed"; + regulator-name = "wlan-en-regulator"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + startup-delay-us= <70000>; + + /* WL_EN */ + gpio = <&gpio3 9 0>; + enable-active-high; + }; + + leds { + pinctrl-names = "default"; + //pinctrl-0 = <&user_leds_s0>; + pinctrl-0 = <&user_leds_s0 &bt_pins>; + + compatible = "gpio-leds"; + + led2 { + label = "beaglebone:green:usr0"; + gpios = <&gpio1 21 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + default-state = "off"; + }; + + led3 { + label = "beaglebone:green:usr1"; + gpios = <&gpio1 22 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "mmc0"; + default-state = "off"; + }; + + led4 { + label = "beaglebone:green:usr2"; + gpios = <&gpio1 23 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "cpu0"; + default-state = "off"; + }; + + led5 { + label = "beaglebone:green:usr3"; + gpios = <&gpio1 24 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "mmc1"; + default-state = "off"; + }; + + led6 { + label = "beaglebone:green:usr4"; + gpios = <&gpio2 21 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "netdev"; + default-state = "off"; + }; + + wl18xx_bt_en: led7 { + label = "wl18xx_bt_en"; + gpios = <&gpio0 28 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + }; +}; + +&cpu0_opp_table { + /* + * Octavo Systems: + * The EFUSE_SMA register is not programmed for any of the AM335x wafers + * we get and we are not programming them during our production test. + * Therefore, from a DEVICE_ID revision point of view, the silicon looks + * like it is Revision 2.1. However, from an EFUSE_SMA point of view for + * the HW OPP table, the silicon looks like it is Revision 1.0 (ie the + * EFUSE_SMA register reads as all zeros). + */ + oppnitro-1000000000 { + opp-supported-hw = <0x06 0x0100>; + }; +}; + +&am33xx_pinmux { + pinctrl-names = "default"; + pinctrl-0 = <&usbhost_pins>; + + user_leds_s0: user_leds_s0 { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_A5, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gpmc_a5.gpio1_21 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_A6, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gpmc_a6.gpio1_22 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_A7, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gpmc_a7.gpio1_23 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_A8, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gpmc_a8.gpio1_24 */ + AM33XX_PADCONF(AM335X_PIN_MII1_RXD0, PIN_OUTPUT_PULLUP, MUX_MODE7) /* WL_Active_LED / USR4 */ + >; + }; + + bt_pins: pinmux_bt_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_TXD0, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gmii1_txd0.gpio0_28 - BT_EN */ + >; + }; + + mmc3_pins: pinmux_mmc3_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_RXD1, PIN_INPUT_PULLUP, MUX_MODE6 ) /* (L15) gmii1_rxd1.mmc2_clk */ + AM33XX_PADCONF(AM335X_PIN_MII1_TX_EN, PIN_INPUT_PULLUP, MUX_MODE6 ) /* (J16) gmii1_txen.mmc2_cmd */ + AM33XX_PADCONF(AM335X_PIN_MII1_RX_DV, PIN_INPUT_PULLUP, MUX_MODE5 ) /* (J17) gmii1_rxdv.mmc2_dat0 */ + AM33XX_PADCONF(AM335X_PIN_MII1_TXD3, PIN_INPUT_PULLUP, MUX_MODE5 ) /* (J18) gmii1_txd3.mmc2_dat1 */ + AM33XX_PADCONF(AM335X_PIN_MII1_TXD2, PIN_INPUT_PULLUP, MUX_MODE5 ) /* (K15) gmii1_txd2.mmc2_dat2 */ + AM33XX_PADCONF(AM335X_PIN_MII1_COL, PIN_INPUT_PULLUP, MUX_MODE5 ) /* (H16) gmii1_col.mmc2_dat3 */ + >; + }; + + uart2_grove_pins: pinmux_uart2_grove_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x90c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE6) + AM33XX_IOPAD(0x910, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE6) + >; + }; + + uart3_pins: pinmux_uart3_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_RXD3, PIN_INPUT_PULLUP, MUX_MODE1) /* gmii1_rxd3.uart3_rxd */ + AM33XX_PADCONF(AM335X_PIN_MII1_RXD2, PIN_OUTPUT_PULLDOWN, MUX_MODE1) /* gmii1_rxd2.uart3_txd */ + AM33XX_PADCONF(AM335X_PIN_MDIO, PIN_INPUT, MUX_MODE3) /* mdio_data.uart3_ctsn */ + AM33XX_PADCONF(AM335X_PIN_MDC, PIN_OUTPUT_PULLDOWN, MUX_MODE3) /* mdio_clk.uart3_rtsn */ + >; + }; + + usbhost_pins: pinmux_usbhost_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_TXD1, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gmii1_txd1.gpio0[21] */ + >; + }; + + wl18xx_pins: pinmux_wl18xx_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_TX_CLK, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gmii1_txclk.gpio3_9 WL_EN */ + AM33XX_PADCONF(AM335X_PIN_RMII1_REF_CLK, PIN_INPUT_PULLDOWN, MUX_MODE7) /* rmii1_refclk.gpio0_29 WL_IRQ */ + AM33XX_PADCONF(AM335X_PIN_MII1_RX_CLK, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gmii1_rxclk.gpio3_10 LS_BUF_EN */ + >; + }; +}; + +&mac { + /delete-property/pinctrl-names; + /delete-property/pinctrl-0; + /delete-property/pinctrl-1; + status = "disabled"; +}; + +&mmc3 { + dmas = <&edma_xbar 12 0 1 + &edma_xbar 13 0 2>; + dma-names = "tx", "rx"; + status = "okay"; + vmmc-supply = <&wlan_en_reg>; + bus-width = <4>; + non-removable; + cap-power-off-card; + keep-power-in-suspend; + pinctrl-names = "default"; + pinctrl-0 = <&mmc3_pins &wl18xx_pins>; + + #address-cells = <1>; + #size-cells = <0>; + wlcore: wlcore@2 { + compatible = "ti,wl1835"; + reg = <2>; + interrupt-parent = <&gpio0>; + interrupts = <29 IRQ_TYPE_EDGE_RISING>; + }; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_grove_pins>; + status = "okay"; +}; + +&uart3 { + pinctrl-names = "default"; + pinctrl-0 = <&uart3_pins>; + //pinctrl-0 = <&uart3_pins &bt_pins>; + status = "okay"; + + //bluetooth { + // compatible = "ti,wl1835-st"; + // enable-gpios = <&gpio0 28 GPIO_ACTIVE_HIGH>; + //}; +}; + +&i2c0 { + #address-cells = <1>; + #size-cells = <0>; + + extrtc: rtc@68 { + compatible = "dallas,ds1340"; + reg = <0x68>; + }; +}; + +// (K16) gmii1_txd1.gpio0[21] +&gpio0 { + usb-reset-hog { + gpio-hog; + gpios = <21 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "usb_reset"; + }; +}; + +&gpio3 { + ls-buf-en-hog { + gpio-hog; + gpios = <10 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "LS_BUF_EN"; + }; +}; + +&usb1 { + #address-cells = <1>; + #size-cells = <0>; + + hub@1 { + compatible = "usb424,9512"; + reg = <1>; + + #address-cells = <1>; + #size-cells = <0>; + + ethernet: ethernet@1 { + compatible = "usb424,ec00"; + reg = <1>; + }; + }; +}; diff --git a/arch/arm/boot/dts/am335x-bonegreen-wireless-common-univ.dtsi b/arch/arm/boot/dts/am335x-bonegreen-wireless-common-univ.dtsi new file mode 100644 index 0000000000000..ea9a993e867cb --- /dev/null +++ b/arch/arm/boot/dts/am335x-bonegreen-wireless-common-univ.dtsi @@ -0,0 +1,2197 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#include + +&am33xx_pinmux { + +/* macro: BONE_PIN( , , */ +#define BONE_PIN(XX,ZZ,QQ) \ + XX##_##ZZ##_pin: pinmux_##XX##_##ZZ##_pin { pinctrl-single,pins = < QQ >; }; + + /************************/ + /* P8 Header */ + /************************/ + + /* P8_01 GND */ + + /* P8_02 GND */ + + + /* P8_03 (ZCZ ball R9) gpmc_ad6 (emmc) */ + BONE_PIN(P8_03, default, P8_03(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_03, gpio, P8_03(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_03, gpio_pu, P8_03(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_03, gpio_pd, P8_03(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + + /* P8_04 (ZCZ ball T9) gpmc_ad7 (emmc) */ + BONE_PIN(P8_04, default, P8_04(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_04, gpio, P8_04(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_04, gpio_pu, P8_04(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_04, gpio_pd, P8_04(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + + /* P8_05 (ZCZ ball R8) gpmc_ad2 (emmc) */ + BONE_PIN(P8_05, default, P8_05(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_05, gpio, P8_05(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_05, gpio_pu, P8_05(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_05, gpio_pd, P8_05(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + + /* P8_06 (ZCZ ball T8) gpmc_ad3 (emmc) */ + BONE_PIN(P8_06, default, P8_06(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_06, gpio, P8_06(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_06, gpio_pu, P8_06(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_06, gpio_pd, P8_06(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + + /* P8_07 (ZCZ ball R7) gpmc_advn_ale (gpio2_2) */ + BONE_PIN(P8_07, default, P8_07(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_07, gpio, P8_07(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_07, gpio_pu, P8_07(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_07, gpio_pd, P8_07(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_07, timer, P8_07(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + + /* P8_08 (ZCZ ball T7) gpmc_oen_ren (gpio2_3) */ + BONE_PIN(P8_08, default, P8_08(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_08, gpio, P8_08(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_08, gpio_pu, P8_08(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_08, gpio_pd, P8_08(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_08, timer, P8_08(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + + /* P8_09 (ZCZ ball T6) gpmc_be0n_cle (gpio2_5) */ + BONE_PIN(P8_09, default, P8_09(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_09, gpio, P8_09(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_09, gpio_pu, P8_09(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_09, gpio_pd, P8_09(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_09, timer, P8_09(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + + /* P8_10 (ZCZ ball U6) gpmc_wen (gpio2_4) */ + BONE_PIN(P8_10, default, P8_10(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_10, gpio, P8_10(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_10, gpio_pu, P8_10(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_10, gpio_pd, P8_10(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_10, timer, P8_10(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + + /* P8_11 (ZCZ ball R12) gpmc_ad13 (gpio1_13) */ + BONE_PIN(P8_11, default, P8_11(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_11, gpio, P8_11(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_11, gpio_pu, P8_11(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_11, gpio_pd, P8_11(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_11, eqep, P8_11(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + BONE_PIN(P8_11, pruout, P8_11(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6)) + + /* P8_12 (ZCZ ball T12) gpmc_ad12 (gpio1_12) */ + BONE_PIN(P8_12, default, P8_12(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_12, gpio, P8_12(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_12, gpio_pu, P8_12(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_12, gpio_pd, P8_12(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_12, eqep, P8_12(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + BONE_PIN(P8_12, pruout, P8_12(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6)) + + /* P8_13 (ZCZ ball T10) gpmc_ad9 (gpio0_23) */ + BONE_PIN(P8_13, default, P8_13(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_13, gpio, P8_13(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_13, gpio_pu, P8_13(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_13, gpio_pd, P8_13(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_13, pwm, P8_13(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE4)) + + /* P8_14 (ZCZ ball T11) wl1835: wl_en */ + + /* P8_15 (ZCZ ball U13) gpmc_ad15 (gpio1_15) */ + BONE_PIN(P8_15, default, P8_15(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_15, gpio, P8_15(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_15, gpio_pu, P8_15(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_15, gpio_pd, P8_15(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_15, eqep, P8_15(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + BONE_PIN(P8_15, pru_ecap_pwm, P8_15(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_15, pruin, P8_15(PIN_INPUT | MUX_MODE6)) + + /* P8_16 (ZCZ ball V13) gpmc_ad14 (gpio1_14) */ + BONE_PIN(P8_16, default, P8_16(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_16, gpio, P8_16(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_16, gpio_pu, P8_16(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_16, gpio_pd, P8_16(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_16, eqep, P8_16(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + BONE_PIN(P8_16, pruin, P8_16(PIN_INPUT | MUX_MODE6)) + + /* P8_17 (ZCZ ball U12) wl1835: wl_irq */ + + /* P8_18 (ZCZ ball V12) gpmc_clk (gpio2_1) */ + BONE_PIN(P8_18, default, P8_18(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_18, gpio, P8_18(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_18, gpio_pu, P8_18(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_18, gpio_pd, P8_18(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + + /* P8_19 (ZCZ ball U10) gpmc_ad8 (gpio0_22) */ + BONE_PIN(P8_19, default, P8_19(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_19, gpio, P8_19(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_19, gpio_pu, P8_19(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_19, gpio_pd, P8_19(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_19, pwm, P8_19(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE4)) + + /* P8_20 (ZCZ ball V9) gpmc_csn2 (emmc) */ + BONE_PIN(P8_20, default, P8_20(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_20, gpio, P8_20(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_20, gpio_pu, P8_20(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_20, gpio_pd, P8_20(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_20, pruout, P8_20(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_20, pruin, P8_20(PIN_INPUT | MUX_MODE6)) + + /* P8_21 (ZCZ ball U9) gpmc_csn1 (emmc) */ + BONE_PIN(P8_21, default, P8_21(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_21, gpio, P8_21(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_21, gpio_pu, P8_21(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_21, gpio_pd, P8_21(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_21, pruout, P8_21(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_21, pruin, P8_21(PIN_INPUT | MUX_MODE6)) + + /* P8_22 (ZCZ ball V8) gpmc_ad5 (emmc) */ + BONE_PIN(P8_22, default, P8_22(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_22, gpio, P8_22(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_22, gpio_pu, P8_22(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_22, gpio_pd, P8_22(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + + /* P8_23 (ZCZ ball U8) gpmc_ad4 (emmc) */ + BONE_PIN(P8_23, default, P8_23(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_23, gpio, P8_23(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_23, gpio_pu, P8_23(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_23, gpio_pd, P8_23(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + + /* P8_24 (ZCZ ball V7) gpmc_ad1 (emmc) */ + BONE_PIN(P8_24, default, P8_24(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_24, gpio, P8_24(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_24, gpio_pu, P8_24(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_24, gpio_pd, P8_24(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + + /* P8_25 (ZCZ ball U7) gpmc_ad0 (emmc) */ + BONE_PIN(P8_25, default, P8_25(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_25, gpio, P8_25(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_25, gpio_pu, P8_25(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_25, gpio_pd, P8_25(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + + /* P8_26 (ZCZ ball V6) gpio-hog wl1835 */ + + /* P8_27 (ZCZ ball U5) lcd_vsync (hdmi) */ + BONE_PIN(P8_27, default, P8_27(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_27, gpio, P8_27(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_27, gpio_pu, P8_27(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_27, gpio_pd, P8_27(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_27, pruout, P8_27(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_27, pruin, P8_27(PIN_INPUT | MUX_MODE6)) + + /* P8_28 (ZCZ ball V5) lcd_pclk (hdmi) */ + BONE_PIN(P8_28, default, P8_28(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_28, gpio, P8_28(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_28, gpio_pu, P8_28(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_28, gpio_pd, P8_28(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_28, pruout, P8_28(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_28, pruin, P8_28(PIN_INPUT | MUX_MODE6)) + + /* P8_29 (ZCZ ball R5) lcd_hsync (hdmi) */ + BONE_PIN(P8_29, default, P8_29(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_29, gpio, P8_29(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_29, gpio_pu, P8_29(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_29, gpio_pd, P8_29(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_29, pruout, P8_29(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_29, pruin, P8_29(PIN_INPUT | MUX_MODE6)) + + /* P8_30 (ZCZ ball R6) lcd_ac_bias_en (hdmi) */ + BONE_PIN(P8_30, default, P8_30(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_30, gpio, P8_30(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_30, gpio_pu, P8_30(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_30, gpio_pd, P8_30(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_30, pruout, P8_30(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_30, pruin, P8_30(PIN_INPUT | MUX_MODE6)) + + /* P8_31 (ZCZ ball V4) lcd_data14 (hdmi) */ + BONE_PIN(P8_31, default, P8_31(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_31, gpio, P8_31(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_31, gpio_pu, P8_31(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_31, gpio_pd, P8_31(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_31, eqep, P8_31(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + BONE_PIN(P8_31, uart, P8_31(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + + /* P8_32 (ZCZ ball T5) lcd_data15 (hdmi) */ + BONE_PIN(P8_32, default, P8_32(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_32, gpio, P8_32(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_32, gpio_pu, P8_32(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_32, gpio_pd, P8_32(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_32, eqep, P8_32(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + + /* P8_33 (ZCZ ball V3) lcd_data13 (hdmi) */ + BONE_PIN(P8_33, default, P8_33(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_33, gpio, P8_33(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_33, gpio_pu, P8_33(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_33, gpio_pd, P8_33(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_33, eqep, P8_33(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + + /* P8_34 (ZCZ ball U4) lcd_data11 (hdmi) */ + BONE_PIN(P8_34, default, P8_34(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_34, gpio, P8_34(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_34, gpio_pu, P8_34(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_34, gpio_pd, P8_34(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_34, pwm, P8_34(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE2)) + + /* P8_35 (ZCZ ball V2) lcd_data12 (hdmi) */ + BONE_PIN(P8_35, default, P8_35(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_35, gpio, P8_35(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_35, gpio_pu, P8_35(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_35, gpio_pd, P8_35(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_35, eqep, P8_35(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + + /* P8_36 (ZCZ ball U3) lcd_data10 (hdmi) */ + BONE_PIN(P8_36, default, P8_36(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_36, gpio, P8_36(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_36, gpio_pu, P8_36(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_36, gpio_pd, P8_36(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_36, pwm, P8_36(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE2)) + + /* P8_37 (ZCZ ball U1) lcd_data8 (hdmi) */ + BONE_PIN(P8_37, default, P8_37(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_37, gpio, P8_37(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_37, gpio_pu, P8_37(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_37, gpio_pd, P8_37(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_37, pwm, P8_37(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE2)) + BONE_PIN(P8_37, uart, P8_37(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + + /* P8_38 (ZCZ ball U2) lcd_data9 (hdmi) */ + BONE_PIN(P8_38, default, P8_38(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_38, gpio, P8_38(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_38, gpio_pu, P8_38(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_38, gpio_pd, P8_38(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_38, pwm, P8_38(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE2)) + BONE_PIN(P8_38, uart, P8_38(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + + /* P8_39 (ZCZ ball T3) lcd_data6 (hdmi) */ + BONE_PIN(P8_39, default, P8_39(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_39, gpio, P8_39(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_39, gpio_pu, P8_39(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_39, gpio_pd, P8_39(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_39, eqep, P8_39(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P8_39, pruout, P8_39(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_39, pruin, P8_39(PIN_INPUT | MUX_MODE6)) + + /* P8_40 (ZCZ ball T4) lcd_data7 (hdmi) */ + BONE_PIN(P8_40, default, P8_40(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_40, gpio, P8_40(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_40, gpio_pu, P8_40(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_40, gpio_pd, P8_40(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_40, eqep, P8_40(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P8_40, pruout, P8_40(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_40, pruin, P8_40(PIN_INPUT | MUX_MODE6)) + + /* P8_41 (ZCZ ball T1) lcd_data4 (hdmi) */ + BONE_PIN(P8_41, default, P8_41(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_41, gpio, P8_41(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_41, gpio_pu, P8_41(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_41, gpio_pd, P8_41(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_41, eqep, P8_41(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P8_41, pruout, P8_41(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_41, pruin, P8_41(PIN_INPUT | MUX_MODE6)) + + /* P8_42 (ZCZ ball T2) lcd_data5 (hdmi) */ + BONE_PIN(P8_42, default, P8_42(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_42, gpio, P8_42(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_42, gpio_pu, P8_42(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_42, gpio_pd, P8_42(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_42, eqep, P8_42(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P8_42, pruout, P8_42(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_42, pruin, P8_42(PIN_INPUT | MUX_MODE6)) + + /* P8_43 (ZCZ ball R3) lcd_data2 (hdmi) */ + BONE_PIN(P8_43, default, P8_43(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_43, gpio, P8_43(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_43, gpio_pu, P8_43(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_43, gpio_pd, P8_43(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_43, pwm, P8_43(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3)) + BONE_PIN(P8_43, pruout, P8_43(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_43, pruin, P8_43(PIN_INPUT | MUX_MODE6)) + + /* P8_44 (ZCZ ball R4) lcd_data3 (hdmi) */ + BONE_PIN(P8_44, default, P8_44(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_44, gpio, P8_44(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_44, gpio_pu, P8_44(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_44, gpio_pd, P8_44(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_44, pwm, P8_44(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3)) + BONE_PIN(P8_44, pruout, P8_44(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_44, pruin, P8_44(PIN_INPUT | MUX_MODE6)) + + /* P8_45 (ZCZ ball R1) lcd_data0 (hdmi) */ + BONE_PIN(P8_45, default, P8_45(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_45, gpio, P8_45(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_45, gpio_pu, P8_45(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_45, gpio_pd, P8_45(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_45, pwm, P8_45(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3)) + BONE_PIN(P8_45, pruout, P8_45(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_45, pruin, P8_45(PIN_INPUT | MUX_MODE6)) + + /* P8_46 (ZCZ ball R2) lcd_data1 (hdmi) */ + BONE_PIN(P8_46, default, P8_46(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_46, gpio, P8_46(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_46, gpio_pu, P8_46(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_46, gpio_pd, P8_46(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P8_46, pwm, P8_46(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3)) + BONE_PIN(P8_46, pruout, P8_46(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P8_46, pruin, P8_46(PIN_INPUT | MUX_MODE6)) + + /************************/ + /* P9 Header */ + /************************/ + + /* P9_01 GND */ + + /* P9_02 GND */ + + /* P9_03 3V3 */ + + /* P9_04 3V3 */ + + /* P9_05 VDD_5V */ + + /* P9_06 VDD_5V */ + + /* P9_07 SYS_5V */ + + /* P9_08 SYS_5V */ + + /* P9_09 PWR_BUT */ + + /* P9_10 RSTn */ + + /* P9_11 (ZCZ ball T17) gpmc_wait0 (gpio0_30) */ + BONE_PIN(P9_11, default, P9_11(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_11, gpio, P9_11(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_11, gpio_pu, P9_11(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_11, gpio_pd, P9_11(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_11, uart, P9_11(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE6)) + + /* P9_12 (ZCZ ball U18) gpmc_be1n (gpio1_28) */ + BONE_PIN(P9_12, default, P9_12(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_12, gpio, P9_12(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_12, gpio_pu, P9_12(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_12, gpio_pd, P9_12(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + + /* P9_13 (ZCZ ball U17) gpmc_wpn (gpio0_31) */ + BONE_PIN(P9_13, default, P9_13(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_13, gpio, P9_13(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_13, gpio_pu, P9_13(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_13, gpio_pd, P9_13(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_13, uart, P9_13(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE6)) + + /* P9_14 (ZCZ ball U14) gpmc_a2 (gpio1_18) */ + BONE_PIN(P9_14, default, P9_14(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_14, gpio, P9_14(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_14, gpio_pu, P9_14(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_14, gpio_pd, P9_14(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_14, pwm, P9_14(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6)) + + /* P9_15 (ZCZ ball R13) gpmc_a0 (gpio1_16) */ + BONE_PIN(P9_15, default, P9_15(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_15, gpio, P9_15(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_15, gpio_pu, P9_15(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_15, gpio_pd, P9_15(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_15, pwm, P9_15(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6)) + + /* P9_16 (ZCZ ball T14) gpmc_a3 (gpio1_19) */ + BONE_PIN(P9_16, default, P9_16(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_16, gpio, P9_16(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_16, gpio_pu, P9_16(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_16, gpio_pd, P9_16(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_16, pwm, P9_16(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6)) + + /* P9_17 (ZCZ ball A16) spi0_cs0 (gpio0_5) */ + BONE_PIN(P9_17, default, P9_17(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_17, gpio, P9_17(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_17, gpio_pu, P9_17(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_17, gpio_pd, P9_17(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_17, spi_cs, P9_17(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0)) + BONE_PIN(P9_17, i2c, P9_17(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + BONE_PIN(P9_17, pwm, P9_17(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3)) + BONE_PIN(P9_17, pru_uart, P9_17(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + + /* P9_18 (ZCZ ball B16) spi0_d1 (gpio0_4) */ + BONE_PIN(P9_18, default, P9_18(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_18, gpio, P9_18(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_18, gpio_pu, P9_18(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_18, gpio_pd, P9_18(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_18, spi, P9_18(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0)) + BONE_PIN(P9_18, i2c, P9_18(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + BONE_PIN(P9_18, pwm, P9_18(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3)) + BONE_PIN(P9_18, pru_uart, P9_18(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + + /* P9_19 (ZCZ ball D17) uart1_rtsn (i2c2_scl) */ + BONE_PIN(P9_19, default, P9_19(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P9_19, gpio, P9_19(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_19, gpio_pu, P9_19(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_19, gpio_pd, P9_19(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_19, timer, P9_19(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P9_19, can, P9_19(PIN_INPUT_PULLUP | MUX_MODE2)) + BONE_PIN(P9_19, i2c, P9_19(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P9_19, spi_cs, P9_19(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + BONE_PIN(P9_19, pru_uart, P9_19(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE5)) + + /* P9_20 (ZCZ ball D18) uart1_ctsn (i2c2_sda) */ + BONE_PIN(P9_20, default, P9_20(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P9_20, gpio, P9_20(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_20, gpio_pu, P9_20(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_20, gpio_pd, P9_20(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_20, timer, P9_20(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P9_20, can, P9_20(PIN_OUTPUT_PULLUP | MUX_MODE2)) + BONE_PIN(P9_20, i2c, P9_20(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P9_20, spi_cs, P9_20(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + BONE_PIN(P9_20, pru_uart, P9_20(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE5)) + + /* P9_21 (ZCZ ball B17) spi0_d0 (gpio0_3) */ + BONE_PIN(P9_21, default, P9_21(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_21, gpio, P9_21(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_21, gpio_pu, P9_21(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_21, gpio_pd, P9_21(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_21, spi, P9_21(PIN_INPUT_PULLUP | MUX_MODE0)) + BONE_PIN(P9_21, uart, P9_21(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P9_21, i2c, P9_21(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + BONE_PIN(P9_21, pwm, P9_21(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3)) + BONE_PIN(P9_21, pru_uart, P9_21(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + + /* P9_22 (ZCZ ball A17) spi0_sclk (gpio0_2) */ + BONE_PIN(P9_22, default, P9_22(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_22, gpio, P9_22(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_22, gpio_pu, P9_22(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_22, gpio_pd, P9_22(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_22, spi_sclk, P9_22(PIN_INPUT_PULLUP | MUX_MODE0)) + BONE_PIN(P9_22, uart, P9_22(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P9_22, i2c, P9_22(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + BONE_PIN(P9_22, pwm, P9_22(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3)) + BONE_PIN(P9_22, pru_uart, P9_22(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + + /* P9_23 (ZCZ ball V14) gpmc_a1 (gpio1_17) */ + BONE_PIN(P9_23, default, P9_23(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_23, gpio, P9_23(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_23, gpio_pu, P9_23(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_23, gpio_pd, P9_23(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_23, pwm, P9_23(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6)) + + /* P9_24 (ZCZ ball D15) uart1_txd (gpio0_15) */ + BONE_PIN(P9_24, default, P9_24(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_24, gpio, P9_24(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_24, gpio_pu, P9_24(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_24, gpio_pd, P9_24(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_24, uart, P9_24(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0)) + BONE_PIN(P9_24, can, P9_24(PIN_INPUT_PULLUP | MUX_MODE2)) + BONE_PIN(P9_24, i2c, P9_24(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P9_24, pru_uart, P9_24(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE5)) + BONE_PIN(P9_24, pruin, P9_24(PIN_INPUT | MUX_MODE6)) + + /* P9_25 (ZCZ ball A14) mcasp0_ahclkx (audio) */ + BONE_PIN(P9_25, default, P9_25(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_25, gpio, P9_25(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_25, gpio_pu, P9_25(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_25, gpio_pd, P9_25(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_25, eqep, P9_25(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P9_25, pruout, P9_25(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P9_25, pruin, P9_25(PIN_INPUT | MUX_MODE6)) + + /* P9_26 (ZCZ ball D16) uart1_rxd (gpio0_14) */ + BONE_PIN(P9_26, default, P9_26(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_26, gpio, P9_26(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_26, gpio_pu, P9_26(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_26, gpio_pd, P9_26(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_26, uart, P9_26(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0)) + BONE_PIN(P9_26, can, P9_26(PIN_OUTPUT_PULLUP | MUX_MODE2)) + BONE_PIN(P9_26, i2c, P9_26(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P9_26, pru_uart, P9_26(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE5)) + BONE_PIN(P9_26, pruin, P9_26(PIN_INPUT | MUX_MODE6)) + + /* P9_27 (ZCZ ball C13) mcasp0_fsr (gpio3_19) */ + BONE_PIN(P9_27, default, P9_27(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_27, gpio, P9_27(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_27, gpio_pu, P9_27(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_27, gpio_pd, P9_27(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_27, eqep, P9_27(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P9_27, pruout, P9_27(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P9_27, pruin, P9_27(PIN_INPUT | MUX_MODE6)) + + /* P9_28 (ZCZ ball C12) mcasp0_ahclkr (audio) */ + BONE_PIN(P9_28, default, P9_28(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_28, gpio, P9_28(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_28, gpio_pu, P9_28(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_28, gpio_pd, P9_28(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_28, pwm, P9_28(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE1)) + BONE_PIN(P9_28, spi_cs, P9_28(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P9_28, pwm2, P9_28(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE4)) + BONE_PIN(P9_28, pruout, P9_28(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P9_28, pruin, P9_28(PIN_INPUT | MUX_MODE6)) + + /* P9_29 (ZCZ ball B13) mcasp0_fsx (audio) */ + BONE_PIN(P9_29, default, P9_29(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_29, gpio, P9_29(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_29, gpio_pu, P9_29(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_29, gpio_pd, P9_29(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_29, pwm, P9_29(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE1)) + BONE_PIN(P9_29, spi, P9_29(PIN_INPUT_PULLUP | MUX_MODE3)) + BONE_PIN(P9_29, pruout, P9_29(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P9_29, pruin, P9_29(PIN_INPUT | MUX_MODE6)) + + /* P9_30 (ZCZ ball D12) gpio-hog wl1835 */ + + /* P9_31 (ZCZ ball A13) mcasp0_aclkx (audio) */ + BONE_PIN(P9_31, default, P9_31(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_31, gpio, P9_31(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_31, gpio_pu, P9_31(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_31, gpio_pd, P9_31(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_31, pwm, P9_31(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE1)) + BONE_PIN(P9_31, spi_sclk, P9_31(PIN_INPUT_PULLUP | MUX_MODE3)) + BONE_PIN(P9_31, pruout, P9_31(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P9_31, pruin, P9_31(PIN_INPUT | MUX_MODE6)) + + /* P9_32 VADC */ + + /* P9_33 (ZCZ ball C8) AIN4 */ + + /* P9_34 AGND */ + + /* P9_35 (ZCZ ball A8) AIN6 */ + + /* P9_36 (ZCZ ball B8) AIN5 */ + + /* P9_37 (ZCZ ball B7) AIN2 */ + + /* P9_38 (ZCZ ball A7) AIN3 */ + + /* P9_39 (ZCZ ball B6) AIN0 */ + + /* P9_40 (ZCZ ball C7) AIN1 */ + + /* P9_41 (ZCZ ball D14) xdma_event_intr1 (gpio0_20) */ + BONE_PIN(P9_41, default, P9_41(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_41, gpio, P9_41(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_41, gpio_pu, P9_41(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_41, gpio_pd, P9_41(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_41, timer, P9_41(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + BONE_PIN(P9_41, pruin, P9_41(PIN_INPUT | MUX_MODE5)) + + /* P9_41.1 */ + /* P9_91 (ZCZ ball D13) mcasp0_axr1 (gpio3_20) */ + BONE_PIN(P9_91, default, P9_91(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_91, gpio, P9_91(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_91, gpio_pu, P9_91(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_91, gpio_pd, P9_91(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_91, eqep, P9_91(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P9_91, pruout, P9_91(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P9_91, pruin, P9_91(PIN_INPUT | MUX_MODE6)) + + /* P9_42 (ZCZ ball C18) eCAP0_in_PWM0_out (gpio0_7) */ + BONE_PIN(P9_42, default, P9_42(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_42, gpio, P9_42(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_42, gpio_pu, P9_42(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_42, gpio_pd, P9_42(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_42, pwm, P9_42(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE0)) + BONE_PIN(P9_42, uart, P9_42(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P9_42, spi_cs, P9_42(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + BONE_PIN(P9_42, pru_ecap_pwm, P9_42(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3)) + BONE_PIN(P9_42, spi_sclk, P9_42(PIN_INPUT_PULLUP | MUX_MODE4)) + + /* P9_42.1 */ + /* P9_92 (ZCZ ball B12) mcasp0_aclkr (gpio3_18) */ + BONE_PIN(P9_92, default, P9_92(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_92, gpio, P9_92(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_92, gpio_pu, P9_92(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_92, gpio_pd, P9_92(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P9_92, eqep, P9_92(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P9_92, pruout, P9_92(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P9_92, pruin, P9_92(PIN_INPUT | MUX_MODE6)) + + /* P9_43 GND */ + + /* P9_44 GND */ + + /* P9_45 GND */ + + /* P9_46 GND */ +}; + +&i2c1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + + clock-frequency = <100000>; + symlink = "bone/i2c/1"; +}; + +&i2c2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + + clock-frequency = <100000>; + symlink = "bone/i2c/2"; +}; + +&uart1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/uart/1"; +}; + +&uart2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/uart/2"; +}; + +&uart3 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/uart/3"; +}; + +&uart4 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/uart/4"; +}; + +&uart5 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/uart/5"; +}; + +&dcan0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/can/0"; +}; + +&dcan1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/can/1"; +}; + +&eqep0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/eqep/0"; +}; + +&eqep1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/eqep/1"; +}; + +&eqep2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + symlink = "bone/eqep/2"; +}; + +&epwmss0 { + status = "okay"; +}; + +&epwmss1 { + status = "okay"; +}; + +&epwmss2 { + status = "okay"; +}; + +&ehrpwm0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + +&ehrpwm1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + +&ehrpwm2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + +&ecap0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + +&ecap1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + +&ecap2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + +&spi0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + + channel@0 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "spidev"; + symlink = "bone/spi/0.0"; + + reg = <0>; + spi-max-frequency = <16000000>; + spi-cpha; + }; + + channel@1 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "spidev"; + symlink = "bone/spi/0.1"; + + reg = <1>; + spi-max-frequency = <16000000>; + }; +}; + +&spi1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + + channel@0 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "spidev"; + symlink = "bone/spi/1.0"; + + reg = <0>; + spi-max-frequency = <16000000>; + spi-cpha; + }; + + channel@1 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "spidev"; + symlink = "bone/spi/1.1"; + + reg = <1>; + spi-max-frequency = <16000000>; + }; +}; + +/**********************************************************************/ +/* Pin Multiplex Helpers */ +/* */ +/* These provide userspace runtime pin configuration for the */ +/* BeagleBone cape expansion headers */ +/**********************************************************************/ + +&ocp { + /************************/ + /* P8 Header */ + /************************/ + + /* P8_01 GND */ + + /* P8_02 GND */ + + + /* P8_03 (ZCZ ball R9) emmc */ + P8_03_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd"; + pinctrl-0 = <&P8_03_default_pin>; + pinctrl-1 = <&P8_03_gpio_pin>; + pinctrl-2 = <&P8_03_gpio_pu_pin>; + pinctrl-3 = <&P8_03_gpio_pd_pin>; + }; + + /* P8_04 (ZCZ ball T9) emmc */ + P8_04_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd"; + pinctrl-0 = <&P8_04_default_pin>; + pinctrl-1 = <&P8_04_gpio_pin>; + pinctrl-2 = <&P8_04_gpio_pu_pin>; + pinctrl-3 = <&P8_04_gpio_pd_pin>; + }; + + /* P8_05 (ZCZ ball R8) emmc */ + P8_05_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd"; + pinctrl-0 = <&P8_05_default_pin>; + pinctrl-1 = <&P8_05_gpio_pin>; + pinctrl-2 = <&P8_05_gpio_pu_pin>; + pinctrl-3 = <&P8_05_gpio_pd_pin>; + }; + + /* P8_06 (ZCZ ball T8) emmc */ + P8_06_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd"; + pinctrl-0 = <&P8_06_default_pin>; + pinctrl-1 = <&P8_06_gpio_pin>; + pinctrl-2 = <&P8_06_gpio_pu_pin>; + pinctrl-3 = <&P8_06_gpio_pd_pin>; + }; + + /* P8_07 (ZCZ ball R7) */ + P8_07_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "timer"; + pinctrl-0 = <&P8_07_default_pin>; + pinctrl-1 = <&P8_07_gpio_pin>; + pinctrl-2 = <&P8_07_gpio_pu_pin>; + pinctrl-3 = <&P8_07_gpio_pd_pin>; + pinctrl-4 = <&P8_07_timer_pin>; + }; + + /* P8_08 (ZCZ ball T7) */ + P8_08_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "timer"; + pinctrl-0 = <&P8_08_default_pin>; + pinctrl-1 = <&P8_08_gpio_pin>; + pinctrl-2 = <&P8_08_gpio_pu_pin>; + pinctrl-3 = <&P8_08_gpio_pd_pin>; + pinctrl-4 = <&P8_08_timer_pin>; + }; + + /* P8_09 (ZCZ ball T6) */ + P8_09_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "timer"; + pinctrl-0 = <&P8_09_default_pin>; + pinctrl-1 = <&P8_09_gpio_pin>; + pinctrl-2 = <&P8_09_gpio_pu_pin>; + pinctrl-3 = <&P8_09_gpio_pd_pin>; + pinctrl-4 = <&P8_09_timer_pin>; + }; + + /* P8_10 (ZCZ ball U6) */ + P8_10_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "timer"; + pinctrl-0 = <&P8_10_default_pin>; + pinctrl-1 = <&P8_10_gpio_pin>; + pinctrl-2 = <&P8_10_gpio_pu_pin>; + pinctrl-3 = <&P8_10_gpio_pd_pin>; + pinctrl-4 = <&P8_10_timer_pin>; + }; + + /* P8_11 (ZCZ ball R12) */ + P8_11_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout"; + pinctrl-0 = <&P8_11_default_pin>; + pinctrl-1 = <&P8_11_gpio_pin>; + pinctrl-2 = <&P8_11_gpio_pu_pin>; + pinctrl-3 = <&P8_11_gpio_pd_pin>; + pinctrl-4 = <&P8_11_eqep_pin>; + pinctrl-5 = <&P8_11_pruout_pin>; + }; + + /* P8_12 (ZCZ ball T12) */ + P8_12_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout"; + pinctrl-0 = <&P8_12_default_pin>; + pinctrl-1 = <&P8_12_gpio_pin>; + pinctrl-2 = <&P8_12_gpio_pu_pin>; + pinctrl-3 = <&P8_12_gpio_pd_pin>; + pinctrl-4 = <&P8_12_eqep_pin>; + pinctrl-5 = <&P8_12_pruout_pin>; + }; + + /* P8_13 (ZCZ ball T10) */ + P8_13_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P8_13_default_pin>; + pinctrl-1 = <&P8_13_gpio_pin>; + pinctrl-2 = <&P8_13_gpio_pu_pin>; + pinctrl-3 = <&P8_13_gpio_pd_pin>; + pinctrl-4 = <&P8_13_pwm_pin>; + }; + + /* P8_14 (ZCZ ball T11) wl1835: wl_en */ + + /* P8_15 (ZCZ ball U13) */ + P8_15_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pru_ecap_pwm", "pruin"; + pinctrl-0 = <&P8_15_default_pin>; + pinctrl-1 = <&P8_15_gpio_pin>; + pinctrl-2 = <&P8_15_gpio_pu_pin>; + pinctrl-3 = <&P8_15_gpio_pd_pin>; + pinctrl-4 = <&P8_15_eqep_pin>; + pinctrl-5 = <&P8_15_pru_ecap_pwm_pin>; + pinctrl-6 = <&P8_15_pruin_pin>; + }; + + /* P8_16 (ZCZ ball V13) */ + P8_16_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruin"; + pinctrl-0 = <&P8_16_default_pin>; + pinctrl-1 = <&P8_16_gpio_pin>; + pinctrl-2 = <&P8_16_gpio_pu_pin>; + pinctrl-3 = <&P8_16_gpio_pd_pin>; + pinctrl-4 = <&P8_16_eqep_pin>; + pinctrl-5 = <&P8_16_pruin_pin>; + }; + + /* P8_17 (ZCZ ball U12) wl1835: wl_irq */ + + /* P8_18 (ZCZ ball V12) */ + P8_18_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd"; + pinctrl-0 = <&P8_18_default_pin>; + pinctrl-1 = <&P8_18_gpio_pin>; + pinctrl-2 = <&P8_18_gpio_pu_pin>; + pinctrl-3 = <&P8_18_gpio_pd_pin>; + }; + + /* P8_19 (ZCZ ball U10) */ + P8_19_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P8_19_default_pin>; + pinctrl-1 = <&P8_19_gpio_pin>; + pinctrl-2 = <&P8_19_gpio_pu_pin>; + pinctrl-3 = <&P8_19_gpio_pd_pin>; + pinctrl-4 = <&P8_19_pwm_pin>; + }; + + /* P8_20 (ZCZ ball V9) emmc */ + P8_20_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_20_default_pin>; + pinctrl-1 = <&P8_20_gpio_pin>; + pinctrl-2 = <&P8_20_gpio_pu_pin>; + pinctrl-3 = <&P8_20_gpio_pd_pin>; + pinctrl-4 = <&P8_20_pruout_pin>; + pinctrl-5 = <&P8_20_pruin_pin>; + }; + + /* P8_21 (ZCZ ball U9) emmc */ + P8_21_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_21_default_pin>; + pinctrl-1 = <&P8_21_gpio_pin>; + pinctrl-2 = <&P8_21_gpio_pu_pin>; + pinctrl-3 = <&P8_21_gpio_pd_pin>; + pinctrl-4 = <&P8_21_pruout_pin>; + pinctrl-5 = <&P8_21_pruin_pin>; + }; + + /* P8_22 (ZCZ ball V8) emmc */ + P8_22_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd"; + pinctrl-0 = <&P8_22_default_pin>; + pinctrl-1 = <&P8_22_gpio_pin>; + pinctrl-2 = <&P8_22_gpio_pu_pin>; + pinctrl-3 = <&P8_22_gpio_pd_pin>; + }; + + /* P8_23 (ZCZ ball U8) emmc */ + P8_23_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd"; + pinctrl-0 = <&P8_23_default_pin>; + pinctrl-1 = <&P8_23_gpio_pin>; + pinctrl-2 = <&P8_23_gpio_pu_pin>; + pinctrl-3 = <&P8_23_gpio_pd_pin>; + }; + + /* P8_24 (ZCZ ball V7) emmc */ + P8_24_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd"; + pinctrl-0 = <&P8_24_default_pin>; + pinctrl-1 = <&P8_24_gpio_pin>; + pinctrl-2 = <&P8_24_gpio_pu_pin>; + pinctrl-3 = <&P8_24_gpio_pd_pin>; + }; + + /* P8_25 (ZCZ ball U7) emmc */ + P8_25_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd"; + pinctrl-0 = <&P8_25_default_pin>; + pinctrl-1 = <&P8_25_gpio_pin>; + pinctrl-2 = <&P8_25_gpio_pu_pin>; + pinctrl-3 = <&P8_25_gpio_pd_pin>; + }; + + /* P8_26 (ZCZ ball V6) gpio-hog wl1835 */ + + /* P8_27 (ZCZ ball U5) hdmi */ + P8_27_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_27_default_pin>; + pinctrl-1 = <&P8_27_gpio_pin>; + pinctrl-2 = <&P8_27_gpio_pu_pin>; + pinctrl-3 = <&P8_27_gpio_pd_pin>; + pinctrl-4 = <&P8_27_pruout_pin>; + pinctrl-5 = <&P8_27_pruin_pin>; + }; + + /* P8_28 (ZCZ ball V5) hdmi */ + P8_28_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_28_default_pin>; + pinctrl-1 = <&P8_28_gpio_pin>; + pinctrl-2 = <&P8_28_gpio_pu_pin>; + pinctrl-3 = <&P8_28_gpio_pd_pin>; + pinctrl-4 = <&P8_28_pruout_pin>; + pinctrl-5 = <&P8_28_pruin_pin>; + }; + + /* P8_29 (ZCZ ball R5) hdmi */ + P8_29_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_29_default_pin>; + pinctrl-1 = <&P8_29_gpio_pin>; + pinctrl-2 = <&P8_29_gpio_pu_pin>; + pinctrl-3 = <&P8_29_gpio_pd_pin>; + pinctrl-4 = <&P8_29_pruout_pin>; + pinctrl-5 = <&P8_29_pruin_pin>; + }; + + /* P8_30 (ZCZ ball R6) hdmi */ + P8_30_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_30_default_pin>; + pinctrl-1 = <&P8_30_gpio_pin>; + pinctrl-2 = <&P8_30_gpio_pu_pin>; + pinctrl-3 = <&P8_30_gpio_pd_pin>; + pinctrl-4 = <&P8_30_pruout_pin>; + pinctrl-5 = <&P8_30_pruin_pin>; + }; + + /* P8_31 (ZCZ ball V4) hdmi */ + P8_31_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart", "eqep"; + pinctrl-0 = <&P8_31_default_pin>; + pinctrl-1 = <&P8_31_gpio_pin>; + pinctrl-2 = <&P8_31_gpio_pu_pin>; + pinctrl-3 = <&P8_31_gpio_pd_pin>; + pinctrl-4 = <&P8_31_uart_pin>; + pinctrl-5 = <&P8_31_eqep_pin>; + }; + + /* P8_32 (ZCZ ball T5) hdmi */ + P8_32_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep"; + pinctrl-0 = <&P8_32_default_pin>; + pinctrl-1 = <&P8_32_gpio_pin>; + pinctrl-2 = <&P8_32_gpio_pu_pin>; + pinctrl-3 = <&P8_32_gpio_pd_pin>; + pinctrl-4 = <&P8_32_eqep_pin>; + }; + + /* P8_33 (ZCZ ball V3) hdmi */ + P8_33_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep"; + pinctrl-0 = <&P8_33_default_pin>; + pinctrl-1 = <&P8_33_gpio_pin>; + pinctrl-2 = <&P8_33_gpio_pu_pin>; + pinctrl-3 = <&P8_33_gpio_pd_pin>; + pinctrl-4 = <&P8_33_eqep_pin>; + }; + + /* P8_34 (ZCZ ball U4) hdmi */ + P8_34_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P8_34_default_pin>; + pinctrl-1 = <&P8_34_gpio_pin>; + pinctrl-2 = <&P8_34_gpio_pu_pin>; + pinctrl-3 = <&P8_34_gpio_pd_pin>; + pinctrl-4 = <&P8_34_pwm_pin>; + }; + + /* P8_35 (ZCZ ball V2) hdmi */ + P8_35_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep"; + pinctrl-0 = <&P8_35_default_pin>; + pinctrl-1 = <&P8_35_gpio_pin>; + pinctrl-2 = <&P8_35_gpio_pu_pin>; + pinctrl-3 = <&P8_35_gpio_pd_pin>; + pinctrl-4 = <&P8_35_eqep_pin>; + }; + + /* P8_36 (ZCZ ball U3) hdmi */ + P8_36_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P8_36_default_pin>; + pinctrl-1 = <&P8_36_gpio_pin>; + pinctrl-2 = <&P8_36_gpio_pu_pin>; + pinctrl-3 = <&P8_36_gpio_pd_pin>; + pinctrl-4 = <&P8_36_pwm_pin>; + }; + + /* P8_37 (ZCZ ball U1) hdmi */ + P8_37_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart", "pwm"; + pinctrl-0 = <&P8_37_default_pin>; + pinctrl-1 = <&P8_37_gpio_pin>; + pinctrl-2 = <&P8_37_gpio_pu_pin>; + pinctrl-3 = <&P8_37_gpio_pd_pin>; + pinctrl-4 = <&P8_37_uart_pin>; + pinctrl-5 = <&P8_37_pwm_pin>; + }; + + /* P8_38 (ZCZ ball U2) hdmi */ + P8_38_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart", "pwm"; + pinctrl-0 = <&P8_38_default_pin>; + pinctrl-1 = <&P8_38_gpio_pin>; + pinctrl-2 = <&P8_38_gpio_pu_pin>; + pinctrl-3 = <&P8_38_gpio_pd_pin>; + pinctrl-4 = <&P8_38_uart_pin>; + pinctrl-5 = <&P8_38_pwm_pin>; + }; + + /* P8_39 (ZCZ ball T3) hdmi */ + P8_39_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout", "pruin"; + pinctrl-0 = <&P8_39_default_pin>; + pinctrl-1 = <&P8_39_gpio_pin>; + pinctrl-2 = <&P8_39_gpio_pu_pin>; + pinctrl-3 = <&P8_39_gpio_pd_pin>; + pinctrl-4 = <&P8_39_eqep_pin>; + pinctrl-5 = <&P8_39_pruout_pin>; + pinctrl-6 = <&P8_39_pruin_pin>; + }; + + /* P8_40 (ZCZ ball T4) hdmi */ + P8_40_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout", "pruin"; + pinctrl-0 = <&P8_40_default_pin>; + pinctrl-1 = <&P8_40_gpio_pin>; + pinctrl-2 = <&P8_40_gpio_pu_pin>; + pinctrl-3 = <&P8_40_gpio_pd_pin>; + pinctrl-4 = <&P8_40_eqep_pin>; + pinctrl-5 = <&P8_40_pruout_pin>; + pinctrl-6 = <&P8_40_pruin_pin>; + }; + + /* P8_41 (ZCZ ball T1) hdmi */ + P8_41_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout", "pruin"; + pinctrl-0 = <&P8_41_default_pin>; + pinctrl-1 = <&P8_41_gpio_pin>; + pinctrl-2 = <&P8_41_gpio_pu_pin>; + pinctrl-3 = <&P8_41_gpio_pd_pin>; + pinctrl-4 = <&P8_41_eqep_pin>; + pinctrl-5 = <&P8_41_pruout_pin>; + pinctrl-6 = <&P8_41_pruin_pin>; + }; + + /* P8_42 (ZCZ ball T2) hdmi */ + P8_42_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout", "pruin"; + pinctrl-0 = <&P8_42_default_pin>; + pinctrl-1 = <&P8_42_gpio_pin>; + pinctrl-2 = <&P8_42_gpio_pu_pin>; + pinctrl-3 = <&P8_42_gpio_pd_pin>; + pinctrl-4 = <&P8_42_eqep_pin>; + pinctrl-5 = <&P8_42_pruout_pin>; + pinctrl-6 = <&P8_42_pruin_pin>; + }; + + /* P8_43 (ZCZ ball R3) hdmi */ + P8_43_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P8_43_default_pin>; + pinctrl-1 = <&P8_43_gpio_pin>; + pinctrl-2 = <&P8_43_gpio_pu_pin>; + pinctrl-3 = <&P8_43_gpio_pd_pin>; + pinctrl-4 = <&P8_43_pwm_pin>; + pinctrl-5 = <&P8_43_pruout_pin>; + pinctrl-6 = <&P8_43_pruin_pin>; + }; + + /* P8_44 (ZCZ ball R4) hdmi */ + P8_44_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P8_44_default_pin>; + pinctrl-1 = <&P8_44_gpio_pin>; + pinctrl-2 = <&P8_44_gpio_pu_pin>; + pinctrl-3 = <&P8_44_gpio_pd_pin>; + pinctrl-4 = <&P8_44_pwm_pin>; + pinctrl-5 = <&P8_44_pruout_pin>; + pinctrl-6 = <&P8_44_pruin_pin>; + }; + + /* P8_45 (ZCZ ball R1) hdmi */ + P8_45_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P8_45_default_pin>; + pinctrl-1 = <&P8_45_gpio_pin>; + pinctrl-2 = <&P8_45_gpio_pu_pin>; + pinctrl-3 = <&P8_45_gpio_pd_pin>; + pinctrl-4 = <&P8_45_pwm_pin>; + pinctrl-5 = <&P8_45_pruout_pin>; + pinctrl-6 = <&P8_45_pruin_pin>; + }; + + /* P8_46 (ZCZ ball R2) hdmi */ + P8_46_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P8_46_default_pin>; + pinctrl-1 = <&P8_46_gpio_pin>; + pinctrl-2 = <&P8_46_gpio_pu_pin>; + pinctrl-3 = <&P8_46_gpio_pd_pin>; + pinctrl-4 = <&P8_46_pwm_pin>; + pinctrl-5 = <&P8_46_pruout_pin>; + pinctrl-6 = <&P8_46_pruin_pin>; + }; + + /************************/ + /* P9 Header */ + /************************/ + + /* P9_01 GND */ + + /* P9_02 GND */ + + /* P9_03 3V3 */ + + /* P9_04 3V3 */ + + /* P9_05 VDD_5V */ + + /* P9_06 VDD_5V */ + + /* P9_07 SYS_5V */ + + /* P9_08 SYS_5V */ + + /* P9_09 PWR_BUT */ + + /* P9_10 RSTn */ + + /* P9_11 (ZCZ ball T17) */ + P9_11_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart"; + pinctrl-0 = <&P9_11_default_pin>; + pinctrl-1 = <&P9_11_gpio_pin>; + pinctrl-2 = <&P9_11_gpio_pu_pin>; + pinctrl-3 = <&P9_11_gpio_pd_pin>; + pinctrl-4 = <&P9_11_uart_pin>; + }; + + /* P9_12 (ZCZ ball U18) */ + P9_12_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd"; + pinctrl-0 = <&P9_12_default_pin>; + pinctrl-1 = <&P9_12_gpio_pin>; + pinctrl-2 = <&P9_12_gpio_pu_pin>; + pinctrl-3 = <&P9_12_gpio_pd_pin>; + }; + + /* P9_13 (ZCZ ball U17) */ + P9_13_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart"; + pinctrl-0 = <&P9_13_default_pin>; + pinctrl-1 = <&P9_13_gpio_pin>; + pinctrl-2 = <&P9_13_gpio_pu_pin>; + pinctrl-3 = <&P9_13_gpio_pd_pin>; + pinctrl-4 = <&P9_13_uart_pin>; + }; + + /* P9_14 (ZCZ ball U14) */ + P9_14_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P9_14_default_pin>; + pinctrl-1 = <&P9_14_gpio_pin>; + pinctrl-2 = <&P9_14_gpio_pu_pin>; + pinctrl-3 = <&P9_14_gpio_pd_pin>; + pinctrl-4 = <&P9_14_pwm_pin>; + }; + + /* P9_15 (ZCZ ball R13) */ + P9_15_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P9_15_default_pin>; + pinctrl-1 = <&P9_15_gpio_pin>; + pinctrl-2 = <&P9_15_gpio_pu_pin>; + pinctrl-3 = <&P9_15_gpio_pd_pin>; + pinctrl-4 = <&P9_15_pwm_pin>; + }; + + /* P9_16 (ZCZ ball T14) */ + P9_16_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P9_16_default_pin>; + pinctrl-1 = <&P9_16_gpio_pin>; + pinctrl-2 = <&P9_16_gpio_pu_pin>; + pinctrl-3 = <&P9_16_gpio_pd_pin>; + pinctrl-4 = <&P9_16_pwm_pin>; + }; + + /* P9_17 (ZCZ ball A16) */ + P9_17_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_cs", "i2c", "pwm", "pru_uart"; + pinctrl-0 = <&P9_17_default_pin>; + pinctrl-1 = <&P9_17_gpio_pin>; + pinctrl-2 = <&P9_17_gpio_pu_pin>; + pinctrl-3 = <&P9_17_gpio_pd_pin>; + pinctrl-4 = <&P9_17_spi_cs_pin>; + pinctrl-5 = <&P9_17_i2c_pin>; + pinctrl-6 = <&P9_17_pwm_pin>; + pinctrl-7 = <&P9_17_pru_uart_pin>; + }; + + /* P9_18 (ZCZ ball B16) */ + P9_18_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi", "i2c", "pwm", "pru_uart"; + pinctrl-0 = <&P9_18_default_pin>; + pinctrl-1 = <&P9_18_gpio_pin>; + pinctrl-2 = <&P9_18_gpio_pu_pin>; + pinctrl-3 = <&P9_18_gpio_pd_pin>; + pinctrl-4 = <&P9_18_spi_pin>; + pinctrl-5 = <&P9_18_i2c_pin>; + pinctrl-6 = <&P9_18_pwm_pin>; + pinctrl-7 = <&P9_18_pru_uart_pin>; + }; + + /* P9_19 (ZCZ ball D17) i2c */ + P9_19_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_cs", "can", "i2c", "pru_uart", "timer"; + pinctrl-0 = <&P9_19_default_pin>; + pinctrl-1 = <&P9_19_gpio_pin>; + pinctrl-2 = <&P9_19_gpio_pu_pin>; + pinctrl-3 = <&P9_19_gpio_pd_pin>; + pinctrl-4 = <&P9_19_spi_cs_pin>; + pinctrl-5 = <&P9_19_can_pin>; + pinctrl-6 = <&P9_19_i2c_pin>; + pinctrl-7 = <&P9_19_pru_uart_pin>; + pinctrl-8 = <&P9_19_timer_pin>; + }; + + /* P9_20 (ZCZ ball D18) i2c */ + P9_20_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_cs", "can", "i2c", "pru_uart", "timer"; + pinctrl-0 = <&P9_20_default_pin>; + pinctrl-1 = <&P9_20_gpio_pin>; + pinctrl-2 = <&P9_20_gpio_pu_pin>; + pinctrl-3 = <&P9_20_gpio_pd_pin>; + pinctrl-4 = <&P9_20_spi_cs_pin>; + pinctrl-5 = <&P9_20_can_pin>; + pinctrl-6 = <&P9_20_i2c_pin>; + pinctrl-7 = <&P9_20_pru_uart_pin>; + pinctrl-8 = <&P9_20_timer_pin>; + }; + + /* P9_21 (ZCZ ball B17) */ + P9_21_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi", "uart", "i2c", "pwm", "pru_uart"; + pinctrl-0 = <&P9_21_default_pin>; + pinctrl-1 = <&P9_21_gpio_pin>; + pinctrl-2 = <&P9_21_gpio_pu_pin>; + pinctrl-3 = <&P9_21_gpio_pd_pin>; + pinctrl-4 = <&P9_21_spi_pin>; + pinctrl-5 = <&P9_21_uart_pin>; + pinctrl-6 = <&P9_21_i2c_pin>; + pinctrl-7 = <&P9_21_pwm_pin>; + pinctrl-8 = <&P9_21_pru_uart_pin>; + }; + + /* P9_22 (ZCZ ball A17) */ + P9_22_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_sclk", "uart", "i2c", "pwm", "pru_uart"; + pinctrl-0 = <&P9_22_default_pin>; + pinctrl-1 = <&P9_22_gpio_pin>; + pinctrl-2 = <&P9_22_gpio_pu_pin>; + pinctrl-3 = <&P9_22_gpio_pd_pin>; + pinctrl-4 = <&P9_22_spi_sclk_pin>; + pinctrl-5 = <&P9_22_uart_pin>; + pinctrl-6 = <&P9_22_i2c_pin>; + pinctrl-7 = <&P9_22_pwm_pin>; + pinctrl-8 = <&P9_22_pru_uart_pin>; + }; + + /* P9_23 (ZCZ ball V14) */ + P9_23_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P9_23_default_pin>; + pinctrl-1 = <&P9_23_gpio_pin>; + pinctrl-2 = <&P9_23_gpio_pu_pin>; + pinctrl-3 = <&P9_23_gpio_pd_pin>; + pinctrl-4 = <&P9_23_pwm_pin>; + }; + + /* P9_24 (ZCZ ball D15) */ + P9_24_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart", "can", "i2c", "pru_uart", "pruin"; + pinctrl-0 = <&P9_24_default_pin>; + pinctrl-1 = <&P9_24_gpio_pin>; + pinctrl-2 = <&P9_24_gpio_pu_pin>; + pinctrl-3 = <&P9_24_gpio_pd_pin>; + pinctrl-4 = <&P9_24_uart_pin>; + pinctrl-5 = <&P9_24_can_pin>; + pinctrl-6 = <&P9_24_i2c_pin>; + pinctrl-7 = <&P9_24_pru_uart_pin>; + pinctrl-8 = <&P9_24_pruin_pin>; + }; + + /* P9_25 (ZCZ ball A14) audio */ + P9_25_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout", "pruin"; + pinctrl-0 = <&P9_25_default_pin>; + pinctrl-1 = <&P9_25_gpio_pin>; + pinctrl-2 = <&P9_25_gpio_pu_pin>; + pinctrl-3 = <&P9_25_gpio_pd_pin>; + pinctrl-4 = <&P9_25_eqep_pin>; + pinctrl-5 = <&P9_25_pruout_pin>; + pinctrl-6 = <&P9_25_pruin_pin>; + }; + + /* P9_26 (ZCZ ball D16) */ + P9_26_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart", "can", "i2c", "pru_uart", "pruin"; + pinctrl-0 = <&P9_26_default_pin>; + pinctrl-1 = <&P9_26_gpio_pin>; + pinctrl-2 = <&P9_26_gpio_pu_pin>; + pinctrl-3 = <&P9_26_gpio_pd_pin>; + pinctrl-4 = <&P9_26_uart_pin>; + pinctrl-5 = <&P9_26_can_pin>; + pinctrl-6 = <&P9_26_i2c_pin>; + pinctrl-7 = <&P9_26_pru_uart_pin>; + pinctrl-8 = <&P9_26_pruin_pin>; + }; + + /* P9_27 (ZCZ ball C13) */ + P9_27_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout", "pruin"; + pinctrl-0 = <&P9_27_default_pin>; + pinctrl-1 = <&P9_27_gpio_pin>; + pinctrl-2 = <&P9_27_gpio_pu_pin>; + pinctrl-3 = <&P9_27_gpio_pd_pin>; + pinctrl-4 = <&P9_27_eqep_pin>; + pinctrl-5 = <&P9_27_pruout_pin>; + pinctrl-6 = <&P9_27_pruin_pin>; + }; + + /* P9_28 (ZCZ ball C12) audio */ + P9_28_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_cs", "pwm", "pwm2", "pruout", "pruin"; + pinctrl-0 = <&P9_28_default_pin>; + pinctrl-1 = <&P9_28_gpio_pin>; + pinctrl-2 = <&P9_28_gpio_pu_pin>; + pinctrl-3 = <&P9_28_gpio_pd_pin>; + pinctrl-4 = <&P9_28_spi_cs_pin>; + pinctrl-5 = <&P9_28_pwm_pin>; + pinctrl-6 = <&P9_28_pwm2_pin>; + pinctrl-7 = <&P9_28_pruout_pin>; + pinctrl-8 = <&P9_28_pruin_pin>; + }; + + /* P9_29 (ZCZ ball B13) audio */ + P9_29_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P9_29_default_pin>; + pinctrl-1 = <&P9_29_gpio_pin>; + pinctrl-2 = <&P9_29_gpio_pu_pin>; + pinctrl-3 = <&P9_29_gpio_pd_pin>; + pinctrl-4 = <&P9_29_spi_pin>; + pinctrl-5 = <&P9_29_pwm_pin>; + pinctrl-6 = <&P9_29_pruout_pin>; + pinctrl-7 = <&P9_29_pruin_pin>; + }; + + /* P9_30 (ZCZ ball D12) gpio-hog wl1835 */ + + /* P9_31 (ZCZ ball A13) audio */ + P9_31_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_sclk", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P9_31_default_pin>; + pinctrl-1 = <&P9_31_gpio_pin>; + pinctrl-2 = <&P9_31_gpio_pu_pin>; + pinctrl-3 = <&P9_31_gpio_pd_pin>; + pinctrl-4 = <&P9_31_spi_sclk_pin>; + pinctrl-5 = <&P9_31_pwm_pin>; + pinctrl-6 = <&P9_31_pruout_pin>; + pinctrl-7 = <&P9_31_pruin_pin>; + }; + + /* P9_32 VADC */ + + /* P9_33 (ZCZ ball C8) AIN4 */ + + /* P9_34 AGND */ + + /* P9_35 (ZCZ ball A8) AIN6 */ + + /* P9_36 (ZCZ ball B8) AIN5 */ + + /* P9_37 (ZCZ ball B7) AIN2 */ + + /* P9_38 (ZCZ ball A7) AIN3 */ + + /* P9_39 (ZCZ ball B6) AIN0 */ + + /* P9_40 (ZCZ ball C7) AIN1 */ + + /* P9_41 (ZCZ ball D14) */ + P9_41_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "timer", "pruin"; + pinctrl-0 = <&P9_41_default_pin>; + pinctrl-1 = <&P9_41_gpio_pin>; + pinctrl-2 = <&P9_41_gpio_pu_pin>; + pinctrl-3 = <&P9_41_gpio_pd_pin>; + pinctrl-4 = <&P9_41_timer_pin>; + pinctrl-5 = <&P9_41_pruin_pin>; + }; + + /* P9_41.1 */ + /* P9_91 (ZCZ ball D13) */ + P9_91_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout", "pruin"; + pinctrl-0 = <&P9_91_default_pin>; + pinctrl-1 = <&P9_91_gpio_pin>; + pinctrl-2 = <&P9_91_gpio_pu_pin>; + pinctrl-3 = <&P9_91_gpio_pd_pin>; + pinctrl-4 = <&P9_91_eqep_pin>; + pinctrl-5 = <&P9_91_pruout_pin>; + pinctrl-6 = <&P9_91_pruin_pin>; + }; + + /* P9_42 (ZCZ ball C18) */ + P9_42_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_cs", "spi_sclk", "uart", "pwm", "pru_ecap_pwm"; + pinctrl-0 = <&P9_42_default_pin>; + pinctrl-1 = <&P9_42_gpio_pin>; + pinctrl-2 = <&P9_42_gpio_pu_pin>; + pinctrl-3 = <&P9_42_gpio_pd_pin>; + pinctrl-4 = <&P9_42_spi_cs_pin>; + pinctrl-5 = <&P9_42_spi_sclk_pin>; + pinctrl-6 = <&P9_42_uart_pin>; + pinctrl-7 = <&P9_42_pwm_pin>; + pinctrl-8 = <&P9_42_pru_ecap_pwm_pin>; + }; + + /* P9_42.1 */ + /* P9_92 (ZCZ ball B12) */ + P9_92_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout", "pruin"; + pinctrl-0 = <&P9_92_default_pin>; + pinctrl-1 = <&P9_92_gpio_pin>; + pinctrl-2 = <&P9_92_gpio_pu_pin>; + pinctrl-3 = <&P9_92_gpio_pd_pin>; + pinctrl-4 = <&P9_92_eqep_pin>; + pinctrl-5 = <&P9_92_pruout_pin>; + pinctrl-6 = <&P9_92_pruin_pin>; + }; + + /* P9_43 GND */ + + /* P9_44 GND */ + + /* P9_45 GND */ + + /* P9_46 GND */ + + cape-universal { + compatible = "gpio-of-helper"; + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + + P8_03 { + gpio-name = "P8_03"; + gpio = <&gpio1 6 0>; + input; + dir-changeable; + }; + + P8_04 { + gpio-name = "P8_04"; + gpio = <&gpio1 7 0>; + input; + dir-changeable; + }; + + P8_05 { + gpio-name = "P8_05"; + gpio = <&gpio1 2 0>; + input; + dir-changeable; + }; + + P8_06 { + gpio-name = "P8_06"; + gpio = <&gpio1 3 0>; + input; + dir-changeable; + }; + + P8_07 { + gpio-name = "P8_07"; + gpio = <&gpio2 2 0>; + input; + dir-changeable; + }; + + P8_08 { + gpio-name = "P8_08"; + gpio = <&gpio2 3 0>; + input; + dir-changeable; + }; + + P8_09 { + gpio-name = "P8_09"; + gpio = <&gpio2 5 0>; + input; + dir-changeable; + }; + + P8_10 { + gpio-name = "P8_10"; + gpio = <&gpio2 4 0>; + input; + dir-changeable; + }; + + P8_11 { + gpio-name = "P8_11"; + gpio = <&gpio1 13 0>; + input; + dir-changeable; + }; + + P8_12 { + gpio-name = "P8_12"; + gpio = <&gpio1 12 0>; + input; + dir-changeable; + }; + + P8_13 { + gpio-name = "P8_13"; + gpio = <&gpio0 23 0>; + input; + dir-changeable; + }; + + P8_15 { + gpio-name = "P8_15"; + gpio = <&gpio1 15 0>; + input; + dir-changeable; + }; + + P8_16 { + gpio-name = "P8_16"; + gpio = <&gpio1 14 0>; + input; + dir-changeable; + }; + + P8_18 { + gpio-name = "P8_18"; + gpio = <&gpio2 1 0>; + input; + dir-changeable; + }; + + P8_19 { + gpio-name = "P8_19"; + gpio = <&gpio0 22 0>; + input; + dir-changeable; + }; + + P8_20 { + gpio-name = "P8_20"; + gpio = <&gpio1 31 0>; + input; + dir-changeable; + }; + + P8_21 { + gpio-name = "P8_21"; + gpio = <&gpio1 30 0>; + input; + dir-changeable; + }; + + P8_22 { + gpio-name = "P8_22"; + gpio = <&gpio1 5 0>; + input; + dir-changeable; + }; + + P8_23 { + gpio-name = "P8_23"; + gpio = <&gpio1 4 0>; + input; + dir-changeable; + }; + + P8_24 { + gpio-name = "P8_24"; + gpio = <&gpio1 1 0>; + input; + dir-changeable; + }; + + P8_25 { + gpio-name = "P8_25"; + gpio = <&gpio1 0 0>; + input; + dir-changeable; + }; + + P8_27 { + gpio-name = "P8_27"; + gpio = <&gpio2 22 0>; + input; + dir-changeable; + }; + + P8_28 { + gpio-name = "P8_28"; + gpio = <&gpio2 24 0>; + input; + dir-changeable; + }; + + P8_29 { + gpio-name = "P8_29"; + gpio = <&gpio2 23 0>; + input; + dir-changeable; + }; + + P8_30 { + gpio-name = "P8_30"; + gpio = <&gpio2 25 0>; + input; + dir-changeable; + }; + + P8_31 { + gpio-name = "P8_31"; + gpio = <&gpio0 10 0>; + input; + dir-changeable; + }; + + P8_32 { + gpio-name = "P8_32"; + gpio = <&gpio0 11 0>; + input; + dir-changeable; + }; + + P8_33 { + gpio-name = "P8_33"; + gpio = <&gpio0 9 0>; + input; + dir-changeable; + }; + + P8_34 { + gpio-name = "P8_34"; + gpio = <&gpio2 17 0>; + input; + dir-changeable; + }; + + P8_35 { + gpio-name = "P8_35"; + gpio = <&gpio0 8 0>; + input; + dir-changeable; + }; + + P8_36 { + gpio-name = "P8_36"; + gpio = <&gpio2 16 0>; + input; + dir-changeable; + }; + + P8_37 { + gpio-name = "P8_37"; + gpio = <&gpio2 14 0>; + input; + dir-changeable; + }; + + P8_38 { + gpio-name = "P8_38"; + gpio = <&gpio2 15 0>; + input; + dir-changeable; + }; + + P8_39 { + gpio-name = "P8_39"; + gpio = <&gpio2 12 0>; + input; + dir-changeable; + }; + + P8_40 { + gpio-name = "P8_40"; + gpio = <&gpio2 13 0>; + input; + dir-changeable; + }; + + P8_41 { + gpio-name = "P8_41"; + gpio = <&gpio2 10 0>; + input; + dir-changeable; + }; + + P8_42 { + gpio-name = "P8_42"; + gpio = <&gpio2 11 0>; + input; + dir-changeable; + }; + + P8_43 { + gpio-name = "P8_43"; + gpio = <&gpio2 8 0>; + input; + dir-changeable; + }; + + P8_44 { + gpio-name = "P8_44"; + gpio = <&gpio2 9 0>; + input; + dir-changeable; + }; + + P8_45 { + gpio-name = "P8_45"; + gpio = <&gpio2 6 0>; + input; + dir-changeable; + }; + + P8_46 { + gpio-name = "P8_46"; + gpio = <&gpio2 7 0>; + input; + dir-changeable; + }; + + P9_11 { + gpio-name = "P9_11"; + gpio = <&gpio0 30 0>; + input; + dir-changeable; + }; + + P9_12 { + gpio-name = "P9_12"; + gpio = <&gpio1 28 0>; + input; + dir-changeable; + }; + + P9_13 { + gpio-name = "P9_13"; + gpio = <&gpio0 31 0>; + input; + dir-changeable; + }; + + P9_14 { + gpio-name = "P9_14"; + gpio = <&gpio1 18 0>; + input; + dir-changeable; + }; + + P9_15 { + gpio-name = "P9_15"; + gpio = <&gpio1 16 0>; + input; + dir-changeable; + }; + + P9_16 { + gpio-name = "P9_16"; + gpio = <&gpio1 19 0>; + input; + dir-changeable; + }; + + P9_17 { + gpio-name = "P9_17"; + gpio = <&gpio0 5 0>; + input; + dir-changeable; + }; + + P9_18 { + gpio-name = "P9_18"; + gpio = <&gpio0 4 0>; + input; + dir-changeable; + }; + + P9_19 { + gpio-name = "P9_19"; + gpio = <&gpio0 13 0>; + input; + dir-changeable; + }; + + P9_20 { + gpio-name = "P9_20"; + gpio = <&gpio0 12 0>; + input; + dir-changeable; + }; + + P9_21 { + gpio-name = "P9_21"; + gpio = <&gpio0 3 0>; + input; + dir-changeable; + }; + + P9_22 { + gpio-name = "P9_22"; + gpio = <&gpio0 2 0>; + input; + dir-changeable; + }; + + P9_23 { + gpio-name = "P9_23"; + gpio = <&gpio1 17 0>; + input; + dir-changeable; + }; + + P9_24 { + gpio-name = "P9_24"; + gpio = <&gpio0 15 0>; + input; + dir-changeable; + }; + + P9_25 { + gpio-name = "P9_25"; + gpio = <&gpio3 21 0>; + input; + dir-changeable; + }; + + P9_26 { + gpio-name = "P9_26"; + gpio = <&gpio0 14 0>; + input; + dir-changeable; + }; + + P9_27 { + gpio-name = "P9_27"; + gpio = <&gpio3 19 0>; + input; + dir-changeable; + }; + + P9_28 { + gpio-name = "P9_28"; + gpio = <&gpio3 17 0>; + input; + dir-changeable; + }; + + P9_29 { + gpio-name = "P9_29"; + gpio = <&gpio3 15 0>; + input; + dir-changeable; + }; + + P9_31 { + gpio-name = "P9_31"; + gpio = <&gpio3 14 0>; + input; + dir-changeable; + }; + + P9_41 { + gpio-name = "P9_41"; + gpio = <&gpio0 20 0>; + input; + dir-changeable; + }; + + P9_91 { + gpio-name = "P9_91"; + gpio = <&gpio3 20 0>; + input; + dir-changeable; + }; + + P9_42 { + gpio-name = "P9_42"; + gpio = <&gpio0 7 0>; + input; + dir-changeable; + }; + + P9_92 { + gpio-name = "P9_92"; + gpio = <&gpio3 18 0>; + input; + dir-changeable; + }; + }; +}; diff --git a/arch/arm/boot/dts/am335x-bonegreen-wireless-uboot-univ.dts b/arch/arm/boot/dts/am335x-bonegreen-wireless-uboot-univ.dts new file mode 100644 index 0000000000000..02fbbe9ece34e --- /dev/null +++ b/arch/arm/boot/dts/am335x-bonegreen-wireless-uboot-univ.dts @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am335x-bonegreen-wireless-common-univ.dtsi" +#include + +/ { + model = "TI AM335x BeagleBone Green Wireless"; + compatible = "ti,am335x-bone-green-wireless", "ti,am335x-bone-green", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + chosen { + base_dtb = "am335x-bonegreen-wireless-uboot-univ.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; + +&gpio1 { + ls-buf-en-hog { + gpio-hog; + gpios = <29 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "LS_BUF_EN"; + }; +}; + +/* BT_AUD_OUT from wl1835 has to be pulled low when WL_EN is activated.*/ +/* in case it isn't, wilink8 ends up in one of the test modes that */ +/* intruces various issues (elp wkaeup timeouts etc.) */ +/* On the BBGW this pin is routed through the level shifter (U21) that */ +/* introduces a pullup on the line and wilink8 ends up in a bad state. */ +/* use a gpio hog to force this pin low. An alternative may be adding */ +/* an external pulldown on U21 pin 4. */ + +&gpio3 { + bt-aud-in-hog { + gpio-hog; + gpios = <16 GPIO_ACTIVE_HIGH>; + output-low; + line-name = "MCASP0_AHCLKR"; + }; +}; diff --git a/arch/arm/boot/dts/am335x-bonegreen-wireless.dts b/arch/arm/boot/dts/am335x-bonegreen-wireless.dts index 7679594539607..24be4ca4ba316 100644 --- a/arch/arm/boot/dts/am335x-bonegreen-wireless.dts +++ b/arch/arm/boot/dts/am335x-bonegreen-wireless.dts @@ -13,6 +13,12 @@ model = "TI AM335x BeagleBone Green Wireless"; compatible = "ti,am335x-bone-green-wireless", "ti,am335x-bone-green", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + chosen { + base_dtb = "am335x-bonegreen-wireless.dts"; + base_dtb_timestamp = __TIMESTAMP__; + wl1835_bt = "S3-texas-300000"; + }; + wlan_en_reg: fixedregulator@2 { compatible = "regulator-fixed"; regulator-name = "wlan-en-regulator"; @@ -24,9 +30,60 @@ gpio = <&gpio0 26 0>; enable-active-high; }; + + leds { + pinctrl-names = "default"; + //pinctrl-0 = <&user_leds_s0>; + pinctrl-0 = <&user_leds_s0 &bt_pins>; + + compatible = "gpio-leds"; + + led2 { + label = "beaglebone:green:usr0"; + gpios = <&gpio1 21 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + default-state = "off"; + }; + + led3 { + label = "beaglebone:green:usr1"; + gpios = <&gpio1 22 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "mmc0"; + default-state = "off"; + }; + + led4 { + label = "beaglebone:green:usr2"; + gpios = <&gpio1 23 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "cpu0"; + default-state = "off"; + }; + + led5 { + label = "beaglebone:green:usr3"; + gpios = <&gpio1 24 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "mmc1"; + default-state = "off"; + }; + + wl18xx_bt_en: led7 { + label = "wl18xx_bt_en"; + gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + }; }; &am33xx_pinmux { + user_leds_s0: user_leds_s0 { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_A5, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gpmc_a5.gpio1_21 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_A6, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gpmc_a6.gpio1_22 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_A7, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gpmc_a7.gpio1_23 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_A8, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gpmc_a8.gpio1_24 */ + >; + }; + bt_pins: pinmux_bt_pins { pinctrl-single,pins = < AM33XX_PADCONF(AM335X_PIN_GPMC_BEN1, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gpmc_ad12.gpio1_28 BT_EN */ @@ -62,7 +119,7 @@ }; }; -&mac_sw { +&mac { status = "disabled"; }; @@ -91,17 +148,18 @@ &uart3 { pinctrl-names = "default"; - pinctrl-0 = <&uart3_pins &bt_pins>; + pinctrl-0 = <&uart3_pins>; + //pinctrl-0 = <&uart3_pins &bt_pins>; status = "okay"; - bluetooth { - compatible = "ti,wl1835-st"; - enable-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>; - }; + //bluetooth { + // compatible = "ti,wl1835-st"; + // enable-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>; + //}; }; &gpio1 { - ls_buf_en { + ls-buf-en-hog { gpio-hog; gpios = <29 GPIO_ACTIVE_HIGH>; output-high; @@ -118,7 +176,7 @@ /* an external pulldown on U21 pin 4. */ &gpio3 { - bt_aud_in { + bt-aud-in-hog { gpio-hog; gpios = <16 GPIO_ACTIVE_HIGH>; output-low; diff --git a/arch/arm/boot/dts/am335x-bonegreen.dts b/arch/arm/boot/dts/am335x-bonegreen.dts index 18cc0f49e999c..62ca9c8764bd0 100644 --- a/arch/arm/boot/dts/am335x-bonegreen.dts +++ b/arch/arm/boot/dts/am335x-bonegreen.dts @@ -11,4 +11,9 @@ / { model = "TI AM335x BeagleBone Green"; compatible = "ti,am335x-bone-green", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + chosen { + base_dtb = "am335x-bonegreen.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; }; diff --git a/arch/arm/boot/dts/am335x-osd3358-sm-red.dts b/arch/arm/boot/dts/am335x-osd3358-sm-red.dts index f841afb27844c..8216016cbea8c 100644 --- a/arch/arm/boot/dts/am335x-osd3358-sm-red.dts +++ b/arch/arm/boot/dts/am335x-osd3358-sm-red.dts @@ -10,13 +10,16 @@ #include "am33xx.dtsi" #include "am335x-osd335x-common.dtsi" -#include - -#include +#include "am335x-boneblack-hdmi.dtsi" / { model = "Octavo Systems OSD3358-SM-RED"; compatible = "oct,osd3358-sm-refdesign", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + chosen { + base_dtb = "am335x-osd3358-sm-red.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; }; &ldo3_reg { @@ -25,10 +28,6 @@ regulator-always-on; }; -&mmc1 { - vmmc-supply = <&vmmcsd_fixed>; -}; - &mmc2 { vmmc-supply = <&vmmcsd_fixed>; pinctrl-names = "default"; @@ -37,110 +36,7 @@ status = "okay"; }; -&am33xx_pinmux { - nxp_hdmi_bonelt_pins: nxp-hdmi-bonelt-pins { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_OUTPUT_PULLDOWN, MUX_MODE3) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA0, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA1, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA2, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA3, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA4, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA5, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA6, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA7, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA8, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA9, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA10, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA11, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA12, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA13, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA14, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_DATA15, PIN_OUTPUT, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_VSYNC, PIN_OUTPUT_PULLDOWN, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_HSYNC, PIN_OUTPUT_PULLDOWN, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_PCLK, PIN_OUTPUT_PULLDOWN, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_LCD_AC_BIAS_EN, PIN_OUTPUT_PULLDOWN, MUX_MODE0) - >; - }; - - nxp_hdmi_bonelt_off_pins: nxp-hdmi-bonelt-off-pins { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_OUTPUT_PULLDOWN, MUX_MODE3) - >; - }; - - mcasp0_pins: mcasp0-pins { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKX, PIN_INPUT_PULLUP, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKR, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mcasp0_ahclkr.mcasp0_axr2*/ - AM33XX_PADCONF(AM335X_PIN_MCASP0_FSX, PIN_OUTPUT_PULLUP, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_MCASP0_ACLKX, PIN_OUTPUT_PULLDOWN, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_GPMC_A11, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gpmc_a11.GPIO1_27 */ - >; - }; - - flash_enable: flash-enable { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_RMII1_REF_CLK, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* rmii1_ref_clk.gpio0_29 */ - >; - }; - - imu_interrupt: imu-interrupt { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_MII1_RX_ER, PIN_INPUT_PULLDOWN, MUX_MODE7) /* mii1_rx_er.gpio3_2 */ - >; - }; - - ethernet_interrupt: ethernet-interrupt{ - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_MII1_COL, PIN_INPUT_PULLDOWN, MUX_MODE7) /* mii1_col.gpio3_0 */ - >; - }; -}; - -&lcdc { - status = "okay"; - - /* If you want to get 24 bit RGB and 16 BGR mode instead of - * current 16 bit RGB and 24 BGR modes, set the propety - * below to "crossed" and uncomment the video-ports -property - * in tda19988 node. - * AM335x errata for wiring: - * https://www.ti.com/lit/er/sprz360i/sprz360i.pdf - */ - - blue-and-red-wiring = "straight"; - - port { - lcdc_0: endpoint { - remote-endpoint = <&hdmi_0>; - }; - }; -}; - &i2c0 { - tda19988: hdmi-encoder@70 { - compatible = "nxp,tda998x"; - reg = <0x70>; - - pinctrl-names = "default", "off"; - pinctrl-0 = <&nxp_hdmi_bonelt_pins>; - pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>; - - /* Convert 24bit BGR to RGB, e.g. cross red and blue wiring */ - /* video-ports = <0x234501>; */ - - #sound-dai-cells = <0>; - audio-ports = < TDA998x_I2S 0x03>; - - port { - hdmi_0: endpoint { - remote-endpoint = <&lcdc_0>; - }; - }; - }; - mpu9250: imu@68 { compatible = "invensense,mpu6050"; reg = <0x68>; @@ -150,7 +46,7 @@ #address-cells = <1>; #size-cells = <0>; ax8975@c { - compatible = "ak,ak8975"; + compatible = "asahi-kasei,ak8975"; reg = <0x0c>; }; }; @@ -167,55 +63,7 @@ }; }; -&rtc { - system-power-controller; -}; - -&mcasp0 { - #sound-dai-cells = <0>; - pinctrl-names = "default"; - pinctrl-0 = <&mcasp0_pins>; - status = "okay"; - op-mode = <0>; /* MCASP_IIS_MODE */ - tdm-slots = <2>; - serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */ - 0 0 1 0 - >; - tx-num-evt = <32>; - rx-num-evt = <32>; -}; - / { - clk_mcasp0_fixed: clk-mcasp0-fixed { - #clock-cells = <0>; - compatible = "fixed-clock"; - clock-frequency = <24576000>; - }; - - clk_mcasp0: clk-mcasp0 { - #clock-cells = <0>; - compatible = "gpio-gate-clock"; - clocks = <&clk_mcasp0_fixed>; - enable-gpios = <&gpio1 27 0>; /* BeagleBone Black Clk enable on GPIO1_27 */ - }; - - sound { - compatible = "simple-audio-card"; - simple-audio-card,name = "TI BeagleBone Black"; - simple-audio-card,format = "i2s"; - simple-audio-card,bitclock-master = <&dailink0_master>; - simple-audio-card,frame-master = <&dailink0_master>; - - dailink0_master: simple-audio-card,cpu { - sound-dai = <&mcasp0>; - clocks = <&clk_mcasp0>; - }; - - simple-audio-card,codec { - sound-dai = <&tda19988>; - }; - }; - chosen { stdout-path = &uart0; }; @@ -264,8 +112,23 @@ }; &am33xx_pinmux { - pinctrl-names = "default"; - pinctrl-0 = <&clkout2_pin>; + flash_enable: flash-enable { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_RMII1_REF_CLK, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* rmii1_ref_clk.gpio0_29 */ + >; + }; + + imu_interrupt: imu-interrupt { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_RX_ER, PIN_INPUT_PULLDOWN, MUX_MODE7) /* mii1_rx_er.gpio3_2 */ + >; + }; + + ethernet_interrupt: ethernet-interrupt{ + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_COL, PIN_INPUT_PULLDOWN, MUX_MODE7) /* mii1_col.gpio3_0 */ + >; + }; user_leds_s0: user-leds-s0 { pinctrl-single,pins = < @@ -290,12 +153,6 @@ >; }; - clkout2_pin: pinmux-clkout2-pin { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR1, PIN_OUTPUT_PULLDOWN, MUX_MODE3) /* xdma_event_intr1.clkout2 */ - >; - }; - cpsw_default: cpsw-default { pinctrl-single,pins = < /* Slave 1 */ @@ -382,6 +239,7 @@ pinctrl-0 = <&uart0_pins>; status = "okay"; + symlink = "bone/uart/0"; }; &usb0 { @@ -399,6 +257,7 @@ pinctrl-0 = <&i2c2_pins>; status = "okay"; clock-frequency = <100000>; + symlink = "bone/i2c/2"; }; &cpsw_emac0 { @@ -427,6 +286,7 @@ &mmc1 { status = "okay"; + vmmc-supply = <&vmmcsd_fixed>; bus-width = <0x4>; pinctrl-names = "default"; pinctrl-0 = <&mmc1_pins>; @@ -434,6 +294,7 @@ }; &rtc { + system-power-controller; clocks = <&clk_32768_ck>, <&clk_24mhz_clkctrl AM3_CLK_24MHZ_CLKDIV32K_CLKCTRL 0>; clock-names = "ext-clk", "int-clk"; }; diff --git a/arch/arm/boot/dts/am335x-osd335x-common.dtsi b/arch/arm/boot/dts/am335x-osd335x-common.dtsi index 2888b15999ee6..49ba87edadbb7 100644 --- a/arch/arm/boot/dts/am335x-osd335x-common.dtsi +++ b/arch/arm/boot/dts/am335x-osd335x-common.dtsi @@ -48,6 +48,7 @@ status = "okay"; clock-frequency = <400000>; + symlink = "bone/i2c/0"; tps: tps@24 { reg = <0x24>; diff --git a/arch/arm/boot/dts/am335x-pocketbeagle.dts b/arch/arm/boot/dts/am335x-pocketbeagle.dts index d526c5941c9b1..77287af2ee4ba 100644 --- a/arch/arm/boot/dts/am335x-pocketbeagle.dts +++ b/arch/arm/boot/dts/am335x-pocketbeagle.dts @@ -8,6 +8,7 @@ #include "am33xx.dtsi" #include "am335x-osd335x-common.dtsi" +#include / { model = "TI AM335x PocketBeagle"; @@ -15,6 +16,8 @@ chosen { stdout-path = &uart0; + base_dtb = "am335x-pocketbeagle.dts"; + base_dtb_timestamp = __TIMESTAMP__; }; leds { @@ -61,51 +64,51 @@ &gpio0 { gpio-line-names = - "[NC]", - "[NC]", + "NC", + "NC", "P1.08 [SPI0_CLK]", "P1.10 [SPI0_MISO]", "P1.12 [SPI0_MOSI]", "P1.06 [SPI0_CS]", "[MMC0_CD]", "P2.29 [SPI1_CLK]", - "[SYSBOOT]", - "[SYSBOOT]", - "[SYSBOOT]", - "[SYSBOOT]", + "[SYSBOOT 12]", + "[SYSBOOT 13]", + "[SYSBOOT 14]", + "[SYSBOOT 15]", "P1.26 [I2C2_SDA]", "P1.28 [I2C2_SCL]", "P2.11 [I2C1_SDA]", "P2.09 [I2C1_SCL]", - "[NC]", - "[NC]", - "[NC]", + "NC", + "NC", + "NC", "P2.31 [SPI1_CS]", "P1.20 [PRU0.16]", - "[NC]", - "[NC]", + "NC", + "NC", "P2.03", - "[NC]", - "[NC]", + "NC", + "NC", "P1.34", "P2.19", - "[NC]", - "[NC]", + "NC", + "NC", "P2.05 [UART4_RX]", "P2.07 [UART4_TX]"; }; &gpio1 { gpio-line-names = - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", "P2.25 [SPI1_MOSI]", "P1.32 [UART0_RX]", "P1.30 [UART0_TX]", @@ -113,10 +116,10 @@ "P2.33", "P2.22", "P2.18", - "[NC]", - "[NC]", + "NC", + "NC", "P2.01 [PWM1A]", - "[NC]", + "NC", "P2.10", "[USR LED 0]", "[USR LED 1]", @@ -126,35 +129,35 @@ "P2.04", "P2.02", "P2.08", - "[NC]", - "[NC]", - "[NC]"; + "NC", + "NC", + "NC"; }; &gpio2 { gpio-line-names = "P2.20", "P2.17", - "[NC]", - "[NC]", - "[NC]", + "NC", + "NC", + "NC", "[EEPROM_WP]", - "[SYSBOOT]", - "[SYSBOOT]", - "[SYSBOOT]", - "[SYSBOOT]", - "[SYSBOOT]", - "[SYSBOOT]", - "[SYSBOOT]", - "[SYSBOOT]", - "[SYSBOOT]", - "[SYSBOOT]", - "[SYSBOOT]", - "[SYSBOOT]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", + "[SYSBOOT 0]", + "[SYSBOOT 1]", + "[SYSBOOT 2]", + "[SYSBOOT 3]", + "[SYSBOOT 4]", + "[SYSBOOT 5]", + "[SYSBOOT 6]", + "[SYSBOOT 7]", + "[SYSBOOT 8]", + "[SYSBOOT 9]", + "[SYSBOOT 10]", + "[SYSBOOT 11]", + "NC", + "NC", + "NC", + "NC", "P2.35 [AIN5]", "P1.02 [AIN6]", "P1.35 [PRU1.10]", @@ -169,19 +172,19 @@ &gpio3 { gpio-line-names = - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", + "NC", + "NC", + "NC", + "NC", + "NC", "[I2C0_SDA]", "[I2C0_SCL]", - "[JTAG]", - "[JTAG]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", + "[JTAG EMU0]", + "[JTAG EMU1]", + "NC", + "NC", + "NC", + "NC", "P1.03 [USB1]", "P1.36 [PWM0A]", "P1.33 [PRU0.1]", @@ -191,162 +194,37 @@ "P2.34 [PRU0.5]", "P2.28 [PRU0.6]", "P1.29 [PRU0.7]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]", - "[NC]"; + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC", + "NC"; }; &am33xx_pinmux { +// i2c2_pins: pinmux-i2c2-pins { +// pinctrl-single,pins = < +// AM33XX_PADCONF(AM335X_PIN_UART1_RTSN, PIN_INPUT_PULLUP, MUX_MODE3) /* (D17) uart1_rtsn.I2C2_SCL */ +// AM33XX_PADCONF(AM335X_PIN_UART1_CTSN, PIN_INPUT_PULLUP, MUX_MODE3) /* (D18) uart1_ctsn.I2C2_SDA */ +// >; +// }; - pinctrl-names = "default"; - - pinctrl-0 = < &P2_03_gpio &P1_34_gpio &P2_19_gpio &P2_24_gpio - &P2_33_gpio &P2_22_gpio &P2_18_gpio &P2_10_gpio - &P2_06_gpio &P2_04_gpio &P2_02_gpio &P2_08_gpio - &P2_17_gpio >; - - /* P2_03 (ZCZ ball T10) gpio0_23 0x824 PIN 9 */ - P2_03_gpio: pinmux_P2_03_gpio { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_AD9, PIN_INPUT_PULLUP, MUX_MODE7) - >; - pinctrl-single,bias-pullup = < 0x10 0x10 0x00 0x18>; - pinctrl-single,bias-pulldown = < 0x10 0x00 0x10 0x18>; - }; - - /* P1_34 (ZCZ ball T11) gpio0_26 0x828 PIN 10 */ - P1_34_gpio: pinmux_P1_34_gpio { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_AD10, PIN_INPUT_PULLUP, MUX_MODE7) - >; - pinctrl-single,bias-pullup = < 0x10 0x10 0x00 0x18>; - pinctrl-single,bias-pulldown = < 0x10 0x00 0x10 0x18>; - }; - - /* P2_19 (ZCZ ball U12) gpio0_27 0x82c PIN 11 */ - P2_19_gpio: pinmux_P2_19_gpio { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_AD11, PIN_INPUT_PULLUP, MUX_MODE7) - >; - pinctrl-single,bias-pullup = < 0x10 0x10 0x00 0x18>; - pinctrl-single,bias-pulldown = < 0x10 0x00 0x10 0x18>; - }; - - /* P2_24 (ZCZ ball T12) gpio1_12 0x830 PIN 12 */ - P2_24_gpio: pinmux_P2_24_gpio { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_AD12, PIN_INPUT_PULLUP, MUX_MODE7) - >; - pinctrl-single,bias-pullup = < 0x10 0x10 0x00 0x18>; - pinctrl-single,bias-pulldown = < 0x10 0x00 0x10 0x18>; - }; - - /* P2_33 (ZCZ ball R12) gpio1_13 0x834 PIN 13 */ - P2_33_gpio: pinmux_P2_33_gpio { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_AD13, PIN_INPUT_PULLUP, MUX_MODE7) - >; - pinctrl-single,bias-pullup = < 0x10 0x10 0x00 0x18>; - pinctrl-single,bias-pulldown = < 0x10 0x00 0x10 0x18>; - }; - - /* P2_22 (ZCZ ball V13) gpio1_14 0x838 PIN 14 */ - P2_22_gpio: pinmux_P2_22_gpio { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_AD14, PIN_INPUT_PULLUP, MUX_MODE7) - >; - pinctrl-single,bias-pullup = < 0x10 0x10 0x00 0x18>; - pinctrl-single,bias-pulldown = < 0x10 0x00 0x10 0x18>; - }; +// ehrpwm0_pins: pinmux-ehrpwm0-pins { +// pinctrl-single,pins = < +// AM33XX_PADCONF(AM335X_PIN_MCASP0_ACLKX, PIN_OUTPUT_PULLDOWN, MUX_MODE1) /* (A13) mcasp0_aclkx.ehrpwm0A */ +// >; +// }; - /* P2_18 (ZCZ ball U13) gpio1_15 0x83c PIN 15 */ - P2_18_gpio: pinmux_P2_18_gpio { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_AD15, PIN_INPUT_PULLUP, MUX_MODE7) - >; - pinctrl-single,bias-pullup = < 0x10 0x10 0x00 0x18>; - pinctrl-single,bias-pulldown = < 0x10 0x00 0x10 0x18>; - }; - - /* P2_10 (ZCZ ball R14) gpio1_20 0x850 PIN 20 */ - P2_10_gpio: pinmux_P2_10_gpio { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_A4, PIN_INPUT_PULLUP, MUX_MODE7) - >; - pinctrl-single,bias-pullup = < 0x10 0x10 0x00 0x18>; - pinctrl-single,bias-pulldown = < 0x10 0x00 0x10 0x18>; - }; - - /* P2_06 (ZCZ ball U16) gpio1_25 0x864 PIN 25 */ - P2_06_gpio: pinmux_P2_06_gpio { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_A9, PIN_INPUT_PULLUP, MUX_MODE7) - >; - pinctrl-single,bias-pullup = < 0x10 0x10 0x00 0x18>; - pinctrl-single,bias-pulldown = < 0x10 0x00 0x10 0x18>; - }; - - /* P2_04 (ZCZ ball T16) gpio1_26 0x868 PIN 26 */ - P2_04_gpio: pinmux_P2_04_gpio { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_A10, PIN_INPUT_PULLUP, MUX_MODE7) - >; - pinctrl-single,bias-pullup = < 0x10 0x10 0x00 0x18>; - pinctrl-single,bias-pulldown = < 0x10 0x00 0x10 0x18>; - }; - - /* P2_02 (ZCZ ball V17) gpio1_27 0x86c PIN 27 */ - P2_02_gpio: pinmux_P2_02_gpio { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_A11, PIN_INPUT_PULLUP, MUX_MODE7) - >; - pinctrl-single,bias-pullup = < 0x10 0x10 0x00 0x18>; - pinctrl-single,bias-pulldown = < 0x10 0x00 0x10 0x18>; - }; - - /* P2_08 (ZCZ ball U18) gpio1_28 0x878 PIN 30 */ - P2_08_gpio: pinmux_P2_08_gpio { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_BEN1, PIN_INPUT_PULLDOWN, MUX_MODE7) - >; - pinctrl-single,bias-pullup = < 0x00 0x10 0x00 0x18>; - pinctrl-single,bias-pulldown = < 0x00 0x00 0x10 0x18>; - }; - - /* P2_17 (ZCZ ball V12) gpio2_1 0x88c PIN 35 */ - P2_17_gpio: pinmux_P2_17_gpio { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_CLK, PIN_INPUT_PULLUP, MUX_MODE7) - >; - pinctrl-single,bias-pullup = < 0x10 0x10 0x00 0x18>; - pinctrl-single,bias-pulldown = < 0x10 0x00 0x10 0x18>; - }; - - i2c2_pins: pinmux-i2c2-pins { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_UART1_RTSN, PIN_INPUT_PULLUP, MUX_MODE3) /* (D17) uart1_rtsn.I2C2_SCL */ - AM33XX_PADCONF(AM335X_PIN_UART1_CTSN, PIN_INPUT_PULLUP, MUX_MODE3) /* (D18) uart1_ctsn.I2C2_SDA */ - >; - }; - - ehrpwm0_pins: pinmux-ehrpwm0-pins { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_MCASP0_ACLKX, PIN_OUTPUT_PULLDOWN, MUX_MODE1) /* (A13) mcasp0_aclkx.ehrpwm0A */ - >; - }; - - ehrpwm1_pins: pinmux-ehrpwm1-pins { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_A2, PIN_OUTPUT_PULLDOWN, MUX_MODE6) /* (U14) gpmc_a2.ehrpwm1A */ - >; - }; +// ehrpwm1_pins: pinmux-ehrpwm1-pins { +// pinctrl-single,pins = < +// AM33XX_PADCONF(AM335X_PIN_GPMC_A2, PIN_OUTPUT_PULLDOWN, MUX_MODE6) /* (U14) gpmc_a2.ehrpwm1A */ +// >; +// }; mmc0_pins: pinmux-mmc0-pins { pinctrl-single,pins = < @@ -360,23 +238,23 @@ >; }; - spi0_pins: pinmux-spi0-pins { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_SPI0_SCLK, PIN_INPUT_PULLUP, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_SPI0_D0, PIN_INPUT_PULLUP, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_SPI0_D1, PIN_INPUT_PULLUP, MUX_MODE0) - AM33XX_PADCONF(AM335X_PIN_SPI0_CS0, PIN_INPUT_PULLUP, MUX_MODE0) - >; - }; +// spi0_pins: pinmux-spi0-pins { +// pinctrl-single,pins = < +// AM33XX_PADCONF(AM335X_PIN_SPI0_SCLK, PIN_INPUT_PULLUP, MUX_MODE0) /* (A17) spi0_sclk.spi0_sclk */ +// AM33XX_PADCONF(AM335X_PIN_SPI0_D0, PIN_INPUT_PULLUP, MUX_MODE0) /* (B17) spi0_d0.spi0_d0 */ +// AM33XX_PADCONF(AM335X_PIN_SPI0_D1, PIN_INPUT_PULLUP, MUX_MODE0) /* (B16) spi0_d1.spi0_d1 */ +// AM33XX_PADCONF(AM335X_PIN_SPI0_CS0, PIN_INPUT_PULLUP, MUX_MODE0) /* (A16) spi0_cs0.spi0_cs0 */ +// >; +// }; - spi1_pins: pinmux-spi1-pins { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_ECAP0_IN_PWM0_OUT, PIN_INPUT_PULLUP, MUX_MODE4) /* (C18) eCAP0_in_PWM0_out.spi1_sclk */ - AM33XX_PADCONF(AM335X_PIN_UART0_CTSN, PIN_INPUT_PULLUP, MUX_MODE4) /* (E18) uart0_ctsn.spi1_d0 */ - AM33XX_PADCONF(AM335X_PIN_UART0_RTSN, PIN_INPUT_PULLUP, MUX_MODE4) /* (E17) uart0_rtsn.spi1_d1 */ - AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_INPUT_PULLUP, MUX_MODE4) /* (A15) xdma_event_intr0.spi1_cs1 */ - >; - }; +// spi1_pins: pinmux-spi1-pins { +// pinctrl-single,pins = < +// AM33XX_PADCONF(AM335X_PIN_ECAP0_IN_PWM0_OUT, PIN_INPUT_PULLUP, MUX_MODE4) /* (C18) eCAP0_in_PWM0_out.spi1_sclk */ +// AM33XX_PADCONF(AM335X_PIN_UART0_CTSN, PIN_INPUT_PULLUP, MUX_MODE4) /* (E18) uart0_ctsn.spi1_d0 */ +// AM33XX_PADCONF(AM335X_PIN_UART0_RTSN, PIN_INPUT_PULLUP, MUX_MODE4) /* (E17) uart0_rtsn.spi1_d1 */ +// AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_INPUT_PULLUP, MUX_MODE4) /* (A15) xdma_event_intr0.spi1_cs1 */ +// >; +// }; usr_leds_pins: pinmux-usr-leds-pins { pinctrl-single,pins = < @@ -394,12 +272,463 @@ >; }; - uart4_pins: pinmux-uart4-pins { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_GPMC_WAIT0, PIN_INPUT_PULLUP, MUX_MODE6) /* (T17) gpmc_wait0.uart4_rxd */ - AM33XX_PADCONF(AM335X_PIN_GPMC_WPN, PIN_OUTPUT_PULLDOWN, MUX_MODE6) /* (U17) gpmc_wpn.uart4_txd */ - >; - }; +// uart4_pins: pinmux-uart4-pins { +// pinctrl-single,pins = < +// AM33XX_PADCONF(AM335X_PIN_GPMC_WAIT0, PIN_INPUT_PULLUP, MUX_MODE6) /* (T17) gpmc_wait0.uart4_rxd */ +// AM33XX_PADCONF(AM335X_PIN_GPMC_WPN, PIN_OUTPUT_PULLDOWN, MUX_MODE6) /* (U17) gpmc_wpn.uart4_txd */ +// >; +// }; + + +/* macro: BONE_PIN( , , */ +#define BONE_PIN(XX,ZZ,QQ) \ + XX##_##ZZ##_pin: pinmux_##XX##_##ZZ##_pin { pinctrl-single,pins = < QQ >; }; + + /************************/ + /* P1 Header */ + /************************/ + + /* P1_01 VIN-AC */ + + /* P1_02 (ZCZ ball R5) lcd_hsync (gpio2_23) */ + BONE_PIN(P1_02, default, P1_02(PIN_INPUT | MUX_MODE7)) + BONE_PIN(P1_02, gpio, P1_02(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_02, gpio_pu, P1_02(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_02, gpio_pd, P1_02(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_02, pruout, P1_02(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P1_02, pruin, P1_02(PIN_INPUT | MUX_MODE6)) + + /* P1_03 (ZCZ ball F15) usb1_vbus_out */ + + /* P1_04 (ZCZ ball R6) lcd_ac_bias_en (gpio2_25) */ + BONE_PIN(P1_04, default, P1_04(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_04, gpio, P1_04(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_04, gpio_pu, P1_04(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_04, gpio_pd, P1_04(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_04, pruout, P1_04(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P1_04, pruin, P1_04(PIN_INPUT | MUX_MODE6)) + + /* P1_05 (ZCZ ball T18) usb1_vbus_in */ + + /* P1_06 (ZCZ ball A16) spi0_cs0 (spi0_cs0) */ + BONE_PIN(P1_06, default, P1_06(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0)) + BONE_PIN(P1_06, gpio, P1_06(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_06, gpio_pu, P1_06(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_06, gpio_pd, P1_06(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_06, spi_cs, P1_06(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0)) + BONE_PIN(P1_06, i2c, P1_06(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + BONE_PIN(P1_06, pwm, P1_06(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3)) + BONE_PIN(P1_06, pru_uart, P1_06(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + + /* P1_07 VIN-USB */ + + /* P1_08 (ZCZ ball A17) spi0_sclk (spi0_sclk) */ + BONE_PIN(P1_08, default, P1_08(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0)) + BONE_PIN(P1_08, gpio, P1_08(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_08, gpio_pu, P1_08(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_08, gpio_pd, P1_08(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_08, spi_sclk, P1_08(PIN_INPUT_PULLUP | MUX_MODE0)) + BONE_PIN(P1_08, uart, P1_08(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P1_08, i2c, P1_08(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + BONE_PIN(P1_08, pwm, P1_08(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3)) + BONE_PIN(P1_08, pru_uart, P1_08(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + + /* P1_09 (ZCZ ball R18) USB1-DN */ + + /* P1_10 (ZCZ ball B17) spi0_d0 (spi0_d0) */ + BONE_PIN(P1_10, default, P1_10(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0)) + BONE_PIN(P1_10, gpio, P1_10(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_10, gpio_pu, P1_10(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_10, gpio_pd, P1_10(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_10, spi, P1_10(PIN_INPUT_PULLUP | MUX_MODE0)) + BONE_PIN(P1_10, uart, P1_10(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P1_10, i2c, P1_10(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + BONE_PIN(P1_10, pwm, P1_10(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3)) + BONE_PIN(P1_10, pru_uart, P1_10(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + + /* P1_11 (ZCZ ball R17) USB1-DP */ + + /* P1_12 (ZCZ ball B16) spi0_d1 (spi0_d1) */ + BONE_PIN(P1_12, default, P1_12(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0)) + BONE_PIN(P1_12, gpio, P1_12(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_12, gpio_pu, P1_12(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_12, gpio_pd, P1_12(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_12, spi, P1_12(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0)) + BONE_PIN(P1_12, i2c, P1_12(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + BONE_PIN(P1_12, pwm, P1_12(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3)) + BONE_PIN(P1_12, pru_uart, P1_12(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + + /* P1_13 (ZCZ ball P17) USB1-ID */ + + /* P1_14 VOUT-3.3V */ + + /* P1_15 GND */ + + /* P1_16 GND */ + + /* P1_17 (ZCZ ball A9) VREFN */ + + /* P1_18 (ZCZ ball B9) VREFP */ + + /* P1_19 (ZCZ ball B6) AIN0 */ + + /* P1_20 (ZCZ ball D14) xdma_event_intr1 (gpio0_20) */ + BONE_PIN(P1_20, default, P1_20(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_20, gpio, P1_20(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_20, gpio_pu, P1_20(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_20, gpio_pd, P1_20(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_20, pruin, P1_20(PIN_INPUT | MUX_MODE5)) + + /* P1_21 (ZCZ ball C7) AIN1 */ + + /* P1_22 GND */ + + /* P1_23 (ZCZ ball B7) AIN2 */ + + /* P1_24 VOUT-5V */ + + /* P1_25 (ZCZ ball A7) AIN3 */ + + /* P1_26 (ZCZ ball D18) uart1_ctsn (i2c2_sda) */ + BONE_PIN(P1_26, default, P1_26(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P1_26, gpio, P1_26(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_26, gpio_pu, P1_26(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_26, gpio_pd, P1_26(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_26, can, P1_26(PIN_OUTPUT_PULLUP | MUX_MODE2)) + BONE_PIN(P1_26, i2c, P1_26(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P1_26, spi_cs, P1_26(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + BONE_PIN(P1_26, pru_uart, P1_26(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE5)) + + /* P1_27 (ZCZ ball C8) AIN4 */ + + /* P1_28 (ZCZ ball D17) uart1_rtsn (i2c2_scl) */ + BONE_PIN(P1_28, default, P1_28(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P1_28, gpio, P1_28(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_28, gpio_pu, P1_28(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_28, gpio_pd, P1_28(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_28, can, P1_28(PIN_INPUT_PULLUP | MUX_MODE2)) + BONE_PIN(P1_28, i2c, P1_28(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P1_28, spi_cs, P1_28(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + BONE_PIN(P1_28, pru_uart, P1_28(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE5)) + + /* P1_29 (ZCZ ball A14) mcasp0_ahclkx (pru0_in7) */ + BONE_PIN(P1_29, default, P1_29(PIN_INPUT | MUX_MODE6)) + BONE_PIN(P1_29, gpio, P1_29(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_29, gpio_pu, P1_29(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_29, gpio_pd, P1_29(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_29, eqep, P1_29(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P1_29, pruout, P1_29(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P1_29, pruin, P1_29(PIN_INPUT | MUX_MODE6)) + + /* P1_30 (ZCZ ball E16) uart0_txd (uart0_txd) */ + BONE_PIN(P1_30, default, P1_30(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0)) + BONE_PIN(P1_30, gpio, P1_30(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_30, gpio_pu, P1_30(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_30, gpio_pd, P1_30(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_30, uart, P1_30(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0)) + BONE_PIN(P1_30, spi_cs, P1_30(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P1_30, can, P1_30(PIN_INPUT_PULLUP | MUX_MODE2)) + BONE_PIN(P1_30, i2c, P1_30(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P1_30, pruout, P1_30(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P1_30, pruin, P1_30(PIN_INPUT | MUX_MODE6)) + + /* P1_31 (ZCZ ball B12) mcasp0_aclkr (pru0_in4) */ + BONE_PIN(P1_31, default, P1_31(PIN_INPUT | MUX_MODE6)) + BONE_PIN(P1_31, gpio, P1_31(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_31, gpio_pu, P1_31(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_31, gpio_pd, P1_31(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_31, eqep, P1_31(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P1_31, pruout, P1_31(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P1_31, pruin, P1_31(PIN_INPUT | MUX_MODE6)) + + /* P1_32 (ZCZ ball E15) uart0_rxd (uart0_rxd) */ + BONE_PIN(P1_32, default, P1_32(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0)) + BONE_PIN(P1_32, gpio, P1_32(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_32, gpio_pu, P1_32(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_32, gpio_pd, P1_32(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_32, uart, P1_32(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0)) + BONE_PIN(P1_32, spi_cs, P1_32(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P1_32, can, P1_32(PIN_OUTPUT_PULLUP | MUX_MODE2)) + BONE_PIN(P1_32, i2c, P1_32(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P1_32, pruout, P1_32(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P1_32, pruin, P1_32(PIN_INPUT | MUX_MODE6)) + + /* P1_33 (ZCZ ball B13) mcasp0_fsx (pru0_in1) */ + BONE_PIN(P1_33, default, P1_33(PIN_INPUT | MUX_MODE6)) + BONE_PIN(P1_33, gpio, P1_33(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_33, gpio_pu, P1_33(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_33, gpio_pd, P1_33(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_33, pwm, P1_33(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE1)) + BONE_PIN(P1_33, spi, P1_33(PIN_INPUT_PULLUP | MUX_MODE3)) + BONE_PIN(P1_33, pruout, P1_33(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P1_33, pruin, P1_33(PIN_INPUT | MUX_MODE6)) + + /* P1_34 (ZCZ ball T11) gpmc_ad10 (gpio0_26) */ + BONE_PIN(P1_34, default, P1_34(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_34, gpio, P1_34(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_34, gpio_pu, P1_34(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_34, gpio_pd, P1_34(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_34, pwm, P1_34(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE4)) + + /* P1_35 (ZCZ ball V5) lcd_pclk (pru1_in10) */ + BONE_PIN(P1_35, default, P1_35(PIN_INPUT | MUX_MODE6)) + BONE_PIN(P1_35, gpio, P1_35(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_35, gpio_pu, P1_35(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_35, gpio_pd, P1_35(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_35, pruout, P1_35(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P1_35, pruin, P1_35(PIN_INPUT | MUX_MODE6)) + + /* P1_36 (ZCZ ball A13) mcasp0_aclkx (ehrpwm0a) */ + BONE_PIN(P1_36, default, P1_36(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE1)) + BONE_PIN(P1_36, gpio, P1_36(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_36, gpio_pu, P1_36(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_36, gpio_pd, P1_36(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P1_36, pwm, P1_36(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE1)) + BONE_PIN(P1_36, spi_sclk, P1_36(PIN_INPUT_PULLUP | MUX_MODE3)) + BONE_PIN(P1_36, pruout, P1_36(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P1_36, pruin, P1_36(PIN_INPUT | MUX_MODE6)) + + + /************************/ + /* P2 Header */ + /************************/ + + /* P2_01 (ZCZ ball U14) gpmc_a2 (ehrpwm1a) */ + BONE_PIN(P2_01, default, P2_01(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6)) + BONE_PIN(P2_01, gpio, P2_01(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_01, gpio_pu, P2_01(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_01, gpio_pd, P2_01(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_01, pwm, P2_01(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6)) + + /* P2_02 (ZCZ ball V17) gpmc_a11 (gpio1_27) */ + BONE_PIN(P2_02, default, P2_02(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_02, gpio, P2_02(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_02, gpio_pu, P2_02(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_02, gpio_pd, P2_02(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + + /* P2_03 (ZCZ ball T10) gpmc_ad9 (gpio0_23) */ + BONE_PIN(P2_03, default, P2_03(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_03, gpio, P2_03(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_03, gpio_pu, P2_03(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_03, gpio_pd, P2_03(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_03, pwm, P2_03(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE4)) + + /* P2_04 (ZCZ ball T16) gpmc_a10 (gpio1_26) */ + BONE_PIN(P2_04, default, P2_04(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_04, gpio, P2_04(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_04, gpio_pu, P2_04(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_04, gpio_pd, P2_04(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + + /* P2_05 (ZCZ ball T17) gpmc_wait0 (uart4_rxd) */ + BONE_PIN(P2_05, default, P2_05(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE6)) + BONE_PIN(P2_05, gpio, P2_05(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_05, gpio_pu, P2_05(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_05, gpio_pd, P2_05(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_05, uart, P2_05(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE6)) + + /* P2_06 (ZCZ ball U16) gpmc_a9 (gpio1_25) */ + BONE_PIN(P2_06, default, P2_06(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_06, gpio, P2_06(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_06, gpio_pu, P2_06(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_06, gpio_pd, P2_06(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + + /* P2_07 (ZCZ ball U17) gpmc_wpn (uart4_txd) */ + BONE_PIN(P2_07, default, P2_07(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE6)) + BONE_PIN(P2_07, gpio, P2_07(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_07, gpio_pu, P2_07(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_07, gpio_pd, P2_07(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_07, uart, P2_07(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE6)) + + /* P2_08 (ZCZ ball U18) gpmc_be1n (gpio1_28) */ + BONE_PIN(P2_08, default, P2_08(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_08, gpio, P2_08(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_08, gpio_pu, P2_08(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_08, gpio_pd, P2_08(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + + /* P2_09 (ZCZ ball D15) uart1_txd (i2c1_scl) */ + BONE_PIN(P2_09, default, P2_09(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P2_09, gpio, P2_09(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_09, gpio_pu, P2_09(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_09, gpio_pd, P2_09(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_09, uart, P2_09(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0)) + BONE_PIN(P2_09, can, P2_09(PIN_INPUT_PULLUP | MUX_MODE2)) + BONE_PIN(P2_09, i2c, P2_09(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P2_09, pru_uart, P2_09(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE5)) + BONE_PIN(P2_09, pruin, P2_09(PIN_INPUT | MUX_MODE6)) + + /* P2_10 (ZCZ ball R14) gpmc_a4 (gpio1_20) */ + BONE_PIN(P2_10, default, P2_10(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_10, gpio, P2_10(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_10, gpio_pu, P2_10(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_10, gpio_pd, P2_10(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_10, eqep, P2_10(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE6)) + + /* P2_11 (ZCZ ball D16) uart1_rxd (i2c1_sda) */ + BONE_PIN(P2_11, default, P2_11(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P2_11, gpio, P2_11(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_11, gpio_pu, P2_11(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_11, gpio_pd, P2_11(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_11, uart, P2_11(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0)) + BONE_PIN(P2_11, can, P2_11(PIN_OUTPUT_PULLUP | MUX_MODE2)) + BONE_PIN(P2_11, i2c, P2_11(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P2_11, pru_uart, P2_11(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE5)) + BONE_PIN(P2_11, pruin, P2_11(PIN_INPUT | MUX_MODE6)) + + /* P2_12 POWER_BUTTON */ + + /* P2_13 VOUT-5V */ + + /* P2_14 BAT-VIN */ + + /* P2_15 GND */ + + /* P2_16 BAT-TEMP */ + + /* P2_17 (ZCZ ball V12) gpmc_clk (gpio2_1) */ + BONE_PIN(P2_17, default, P2_17(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_17, gpio, P2_17(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_17, gpio_pu, P2_17(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_17, gpio_pd, P2_17(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + + /* P2_18 (ZCZ ball U13) gpmc_ad15 (gpio1_15) */ + BONE_PIN(P2_18, default, P2_18(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_18, gpio, P2_18(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_18, gpio_pu, P2_18(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_18, gpio_pd, P2_18(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_18, eqep, P2_18(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + BONE_PIN(P2_18, pru_ecap_pwm, P2_18(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P2_18, pruin, P2_18(PIN_INPUT | MUX_MODE6)) + + /* P2_19 (ZCZ ball U12) gpmc_ad11 (gpio0_27) */ + BONE_PIN(P2_19, default, P2_19(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_19, gpio, P2_19(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_19, gpio_pu, P2_19(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_19, gpio_pd, P2_19(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_19, pwm, P2_19(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE4)) + + /* P2_20 (ZCZ ball T13) gpmc_csn3 (gpio2_0) */ + BONE_PIN(P2_20, default, P2_20(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_20, gpio, P2_20(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_20, gpio_pu, P2_20(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_20, gpio_pd, P2_20(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + + /* P2_21 GND */ + + /* P2_22 (ZCZ ball V13) gpmc_ad14 (gpio1_14) */ + BONE_PIN(P2_22, default, P2_22(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_22, gpio, P2_22(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_22, gpio_pu, P2_22(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_22, gpio_pd, P2_22(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_22, eqep, P2_22(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + BONE_PIN(P2_22, pruin, P2_22(PIN_INPUT | MUX_MODE6)) + + /* P2_23 VOUT-3.3V */ + + /* P2_24 (ZCZ ball T12) gpmc_ad12 (gpio1_12) */ + BONE_PIN(P2_24, default, P2_24(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_24, gpio, P2_24(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_24, gpio_pu, P2_24(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_24, gpio_pd, P2_24(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_24, eqep, P2_24(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + BONE_PIN(P2_24, pruout, P2_24(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6)) + + /* P2_25 (ZCZ ball E17) uart0_rtsn (spi1_d1) */ + BONE_PIN(P2_25, default, P2_25(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + BONE_PIN(P2_25, gpio, P2_25(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_25, gpio_pu, P2_25(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_25, gpio_pd, P2_25(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_25, uart, P2_25(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P2_25, can, P2_25(PIN_INPUT_PULLUP | MUX_MODE2)) + BONE_PIN(P2_25, i2c, P2_25(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P2_25, spi, P2_25(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + BONE_PIN(P2_25, spi_cs, P2_25(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE5)) + + /* P2_26 RESET# */ + + /* P2_27 (ZCZ ball E18) uart0_ctsn (spi1_d0) */ + BONE_PIN(P2_27, default, P2_27(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + BONE_PIN(P2_27, gpio, P2_27(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_27, gpio_pu, P2_27(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_27, gpio_pd, P2_27(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_27, uart, P2_27(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P2_27, can, P2_27(PIN_OUTPUT_PULLUP | MUX_MODE2)) + BONE_PIN(P2_27, i2c, P2_27(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P2_27, spi, P2_27(PIN_INPUT_PULLUP | MUX_MODE4)) + + /* P2_28 (ZCZ ball D13) mcasp0_axr1 (pru0_in6) */ + BONE_PIN(P2_28, default, P2_28(PIN_INPUT | MUX_MODE6)) + BONE_PIN(P2_28, gpio, P2_28(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_28, gpio_pu, P2_28(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_28, gpio_pd, P2_28(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_28, eqep, P2_28(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P2_28, pruout, P2_28(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P2_28, pruin, P2_28(PIN_INPUT | MUX_MODE6)) + + /* P2_29 (ZCZ ball C18) eCAP0_in_PWM0_out (spi1_sclk) */ + BONE_PIN(P2_29, default, P2_29(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + BONE_PIN(P2_29, gpio, P2_29(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_29, gpio_pu, P2_29(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_29, gpio_pd, P2_29(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_29, pwm, P2_29(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE0)) + BONE_PIN(P2_29, uart, P2_29(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P2_29, spi_cs, P2_29(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE2)) + BONE_PIN(P2_29, pru_ecap_pwm, P2_29(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE3)) + BONE_PIN(P2_29, spi_sclk, P2_29(PIN_INPUT_PULLUP | MUX_MODE4)) + + /* P2_30 (ZCZ ball C12) mcasp0_ahclkr (pru0_in3) */ + BONE_PIN(P2_30, default, P2_30(PIN_INPUT | MUX_MODE6)) + BONE_PIN(P2_30, gpio, P2_30(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_30, gpio_pu, P2_30(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_30, gpio_pd, P2_30(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_30, pwm, P2_30(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE1)) + BONE_PIN(P2_30, spi_cs, P2_30(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P2_30, pruout, P2_30(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P2_30, pruin, P2_30(PIN_INPUT | MUX_MODE6)) + + /* P2_31 (ZCZ ball A15) xdma_event_intr0 (spi1_cs1) */ + BONE_PIN(P2_31, default, P2_31(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + BONE_PIN(P2_31, gpio, P2_31(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_31, gpio_pu, P2_31(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_31, gpio_pd, P2_31(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_31, spi_cs, P2_31(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + BONE_PIN(P2_31, pruin, P2_31(PIN_INPUT | MUX_MODE5)) + + /* P2_32 (ZCZ ball D12) mcasp0_axr0 (pru0_in2) */ + BONE_PIN(P2_32, default, P2_32(PIN_INPUT | MUX_MODE6)) + BONE_PIN(P2_32, gpio, P2_32(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_32, gpio_pu, P2_32(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_32, gpio_pd, P2_32(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_32, pwm, P2_32(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE1)) + BONE_PIN(P2_32, spi, P2_32(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3)) + BONE_PIN(P2_32, pruout, P2_32(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P2_32, pruin, P2_32(PIN_INPUT | MUX_MODE6)) + + /* P2_33 (ZCZ ball R12) gpmc_ad13 (gpio1_13) */ + BONE_PIN(P2_33, default, P2_33(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_33, gpio, P2_33(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_33, gpio_pu, P2_33(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_33, gpio_pd, P2_33(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_33, eqep, P2_33(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4)) + BONE_PIN(P2_33, pruout, P2_33(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE6)) + + /* P2_34 (ZCZ ball C13) mcasp0_fsr (pru0_in5) */ + BONE_PIN(P2_34, default, P2_34(PIN_INPUT | MUX_MODE6)) + BONE_PIN(P2_34, gpio, P2_34(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_34, gpio_pu, P2_34(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_34, gpio_pd, P2_34(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_34, eqep, P2_34(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1)) + BONE_PIN(P2_34, pruout, P2_34(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P2_34, pruin, P2_34(PIN_INPUT | MUX_MODE6)) + + /* P2_35 (ZCZ ball U5) lcd_vsync (gpio2_22) */ + BONE_PIN(P2_35, default, P2_35(PIN_INPUT | MUX_MODE7)) + BONE_PIN(P2_35, gpio, P2_35(PIN_OUTPUT | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_35, gpio_pu, P2_35(PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_35, gpio_pd, P2_35(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE7)) + BONE_PIN(P2_35, pruout, P2_35(PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE5)) + BONE_PIN(P2_35, pruin, P2_35(PIN_INPUT | MUX_MODE6)) + + /* P2_36 (ZCZ ball C9) AIN7 */ }; &epwmss0 { @@ -409,7 +738,8 @@ &ehrpwm0 { status = "okay"; pinctrl-names = "default"; - pinctrl-0 = <&ehrpwm0_pins>; + //pinctrl-0 = <&ehrpwm0_pins>; + pinctrl-0 = <>; }; &epwmss1 { @@ -419,7 +749,18 @@ &ehrpwm1 { status = "okay"; pinctrl-names = "default"; - pinctrl-0 = <&ehrpwm1_pins>; + //pinctrl-0 = <&ehrpwm1_pins>; + pinctrl-0 = <>; +}; + +&epwmss2 { + status = "okay"; +}; + +&ehrpwm2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; }; &i2c0 { @@ -429,9 +770,18 @@ }; }; +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <>; + + status = "okay"; + clock-frequency = <400000>; +}; + &i2c2 { pinctrl-names = "default"; - pinctrl-0 = <&i2c2_pins>; +// pinctrl-0 = <&i2c2_pins>; + pinctrl-0 = <>; status = "okay"; clock-frequency = <400000>; @@ -450,6 +800,10 @@ system-power-controller; }; +&pruss_tm { + status = "okay"; +}; + &tscadc { status = "okay"; adc { @@ -462,14 +816,30 @@ &uart0 { pinctrl-names = "default"; - pinctrl-0 = <&uart0_pins>; + //pinctrl-0 = <&uart0_pins>; + pinctrl-0 = <>; + + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <>; + + status = "okay"; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <>; status = "okay"; }; &uart4 { pinctrl-names = "default"; - pinctrl-0 = <&uart4_pins>; + //pinctrl-0 = <&uart4_pins>; + pinctrl-0 = <>; status = "okay"; }; @@ -481,3 +851,1048 @@ &usb1 { dr_mode = "host"; }; + +&spi0 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + channel@0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "rohm,dh2228fv"; + symlink = "bone/spi/0.0"; + reg = <0>; + spi-max-frequency = <24000000>; + }; + + channel@1 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "rohm,dh2228fv"; + symlink = "bone/spi/0.1"; + reg = <1>; + spi-max-frequency = <24000000>; + status = "disabled"; + }; +}; + +&spi1 { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + channel@0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "rohm,dh2228fv"; + symlink = "bone/spi/1.0"; + reg = <0>; + spi-max-frequency = <24000000>; + }; + + channel@1 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "rohm,dh2228fv"; + symlink = "bone/spi/1.1"; + reg = <1>; + spi-max-frequency = <24000000>; + }; +}; + +&dcan0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + +&dcan1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; +}; + +&ocp { + /************************/ + /* P1 Header */ + /************************/ + + /* P1_01 VIN-AC */ + + /* P1_02 (ZCZ ball R5) gpio */ + P1_02_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P1_02_default_pin>; + pinctrl-1 = <&P1_02_gpio_pin>; + pinctrl-2 = <&P1_02_gpio_pu_pin>; + pinctrl-3 = <&P1_02_gpio_pd_pin>; + pinctrl-4 = <&P1_02_pruout_pin>; + pinctrl-5 = <&P1_02_pruin_pin>; + }; + + /* P1_03 (ZCZ ball F15) usb1_vbus_out */ + + /* P1_04 (ZCZ ball R6) */ + P1_04_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P1_04_default_pin>; + pinctrl-1 = <&P1_04_gpio_pin>; + pinctrl-2 = <&P1_04_gpio_pu_pin>; + pinctrl-3 = <&P1_04_gpio_pd_pin>; + pinctrl-4 = <&P1_04_pruout_pin>; + pinctrl-5 = <&P1_04_pruin_pin>; + }; + + /* P1_05 (ZCZ ball T18) usb1_vbus_in */ + + /* P1_06 (ZCZ ball A16) spi_cs */ + P1_06_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_cs", "i2c", "pwm", "pru_uart"; + pinctrl-0 = <&P1_06_default_pin>; + pinctrl-1 = <&P1_06_gpio_pin>; + pinctrl-2 = <&P1_06_gpio_pu_pin>; + pinctrl-3 = <&P1_06_gpio_pd_pin>; + pinctrl-4 = <&P1_06_spi_cs_pin>; + pinctrl-5 = <&P1_06_i2c_pin>; + pinctrl-6 = <&P1_06_pwm_pin>; + pinctrl-7 = <&P1_06_pru_uart_pin>; + }; + + /* P1_07 VIN-USB */ + + /* P1_08 (ZCZ ball A17) spi_sclk */ + P1_08_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_sclk", "uart", "i2c", "pwm", "pru_uart"; + pinctrl-0 = <&P1_08_default_pin>; + pinctrl-1 = <&P1_08_gpio_pin>; + pinctrl-2 = <&P1_08_gpio_pu_pin>; + pinctrl-3 = <&P1_08_gpio_pd_pin>; + pinctrl-4 = <&P1_08_spi_sclk_pin>; + pinctrl-5 = <&P1_08_uart_pin>; + pinctrl-6 = <&P1_08_i2c_pin>; + pinctrl-7 = <&P1_08_pwm_pin>; + pinctrl-8 = <&P1_08_pru_uart_pin>; + }; + + /* P1_09 (ZCZ ball R18) USB1-DN */ + + /* P1_10 (ZCZ ball B17) spi */ + P1_10_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi", "uart", "i2c", "pwm", "pru_uart"; + pinctrl-0 = <&P1_10_default_pin>; + pinctrl-1 = <&P1_10_gpio_pin>; + pinctrl-2 = <&P1_10_gpio_pu_pin>; + pinctrl-3 = <&P1_10_gpio_pd_pin>; + pinctrl-4 = <&P1_10_spi_pin>; + pinctrl-5 = <&P1_10_uart_pin>; + pinctrl-6 = <&P1_10_i2c_pin>; + pinctrl-7 = <&P1_10_pwm_pin>; + pinctrl-8 = <&P1_10_pru_uart_pin>; + }; + + /* P1_11 (ZCZ ball R17) USB1-DP */ + + /* P1_12 (ZCZ ball B16) spi */ + P1_12_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi", "i2c", "pwm", "pru_uart"; + pinctrl-0 = <&P1_12_default_pin>; + pinctrl-1 = <&P1_12_gpio_pin>; + pinctrl-2 = <&P1_12_gpio_pu_pin>; + pinctrl-3 = <&P1_12_gpio_pd_pin>; + pinctrl-4 = <&P1_12_spi_pin>; + pinctrl-5 = <&P1_12_i2c_pin>; + pinctrl-6 = <&P1_12_pwm_pin>; + pinctrl-7 = <&P1_12_pru_uart_pin>; + }; + + /* P1_13 (ZCZ ball P17) USB1-ID */ + + /* P1_14 VOUT-3.3V */ + + /* P1_15 GND */ + + /* P1_16 GND */ + + /* P1_17 (ZCZ ball A9) VREFN */ + + /* P1_18 (ZCZ ball B9) VREFP */ + + /* P1_19 (ZCZ ball B6) AIN0 */ + + /* P1_20 (ZCZ ball D14) */ + P1_20_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruin"; + pinctrl-0 = <&P1_20_default_pin>; + pinctrl-1 = <&P1_20_gpio_pin>; + pinctrl-2 = <&P1_20_gpio_pu_pin>; + pinctrl-3 = <&P1_20_gpio_pd_pin>; + pinctrl-4 = <&P1_20_pruin_pin>; + }; + + /* P1_21 (ZCZ ball C7) AIN1 */ + + /* P1_22 GND */ + + /* P1_23 (ZCZ ball B7) AIN2 */ + + /* P1_24 VOUT-5V */ + + /* P1_25 (ZCZ ball A7) AIN3 */ + + /* P1_26 (ZCZ ball D18) i2c */ + P1_26_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_cs", "can", "i2c", "pru_uart"; + pinctrl-0 = <&P1_26_default_pin>; + pinctrl-1 = <&P1_26_gpio_pin>; + pinctrl-2 = <&P1_26_gpio_pu_pin>; + pinctrl-3 = <&P1_26_gpio_pd_pin>; + pinctrl-4 = <&P1_26_spi_cs_pin>; + pinctrl-5 = <&P1_26_can_pin>; + pinctrl-6 = <&P1_26_i2c_pin>; + pinctrl-7 = <&P1_26_pru_uart_pin>; + }; + + /* P1_27 (ZCZ ball C8) AIN4 */ + + /* P1_28 (ZCZ ball D17) i2c */ + P1_28_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_cs", "can", "i2c", "pru_uart"; + pinctrl-0 = <&P1_28_default_pin>; + pinctrl-1 = <&P1_28_gpio_pin>; + pinctrl-2 = <&P1_28_gpio_pu_pin>; + pinctrl-3 = <&P1_28_gpio_pd_pin>; + pinctrl-4 = <&P1_28_spi_cs_pin>; + pinctrl-5 = <&P1_28_can_pin>; + pinctrl-6 = <&P1_28_i2c_pin>; + pinctrl-7 = <&P1_28_pru_uart_pin>; + }; + + /* P1_29 (ZCZ ball A14) pruin */ + P1_29_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout", "pruin"; + pinctrl-0 = <&P1_29_default_pin>; + pinctrl-1 = <&P1_29_gpio_pin>; + pinctrl-2 = <&P1_29_gpio_pu_pin>; + pinctrl-3 = <&P1_29_gpio_pd_pin>; + pinctrl-4 = <&P1_29_eqep_pin>; + pinctrl-5 = <&P1_29_pruout_pin>; + pinctrl-6 = <&P1_29_pruin_pin>; + }; + + /* P1_30 (ZCZ ball E16) uart */ + P1_30_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_cs", "uart", "can", "i2c", "pruout", "pruin"; + pinctrl-0 = <&P1_30_default_pin>; + pinctrl-1 = <&P1_30_gpio_pin>; + pinctrl-2 = <&P1_30_gpio_pu_pin>; + pinctrl-3 = <&P1_30_gpio_pd_pin>; + pinctrl-4 = <&P1_30_spi_cs_pin>; + pinctrl-5 = <&P1_30_uart_pin>; + pinctrl-6 = <&P1_30_can_pin>; + pinctrl-7 = <&P1_30_i2c_pin>; + pinctrl-8 = <&P1_30_pruout_pin>; + pinctrl-9 = <&P1_30_pruin_pin>; + }; + + /* P1_31 (ZCZ ball B12) pruin */ + P1_31_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout", "pruin"; + pinctrl-0 = <&P1_31_default_pin>; + pinctrl-1 = <&P1_31_gpio_pin>; + pinctrl-2 = <&P1_31_gpio_pu_pin>; + pinctrl-3 = <&P1_31_gpio_pd_pin>; + pinctrl-4 = <&P1_31_eqep_pin>; + pinctrl-5 = <&P1_31_pruout_pin>; + pinctrl-6 = <&P1_31_pruin_pin>; + }; + + /* P1_32 (ZCZ ball E15) uart */ + P1_32_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_cs", "uart", "can", "i2c", "pruout", "pruin"; + pinctrl-0 = <&P1_32_default_pin>; + pinctrl-1 = <&P1_32_gpio_pin>; + pinctrl-2 = <&P1_32_gpio_pu_pin>; + pinctrl-3 = <&P1_32_gpio_pd_pin>; + pinctrl-4 = <&P1_32_spi_cs_pin>; + pinctrl-5 = <&P1_32_uart_pin>; + pinctrl-6 = <&P1_32_can_pin>; + pinctrl-7 = <&P1_32_i2c_pin>; + pinctrl-8 = <&P1_32_pruout_pin>; + pinctrl-9 = <&P1_32_pruin_pin>; + }; + + /* P1_33 (ZCZ ball B13) pruin */ + P1_33_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P1_33_default_pin>; + pinctrl-1 = <&P1_33_gpio_pin>; + pinctrl-2 = <&P1_33_gpio_pu_pin>; + pinctrl-3 = <&P1_33_gpio_pd_pin>; + pinctrl-4 = <&P1_33_spi_pin>; + pinctrl-5 = <&P1_33_pwm_pin>; + pinctrl-6 = <&P1_33_pruout_pin>; + pinctrl-7 = <&P1_33_pruin_pin>; + }; + + /* P1_34 (ZCZ ball T11) */ + P1_34_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P1_34_default_pin>; + pinctrl-1 = <&P1_34_gpio_pin>; + pinctrl-2 = <&P1_34_gpio_pu_pin>; + pinctrl-3 = <&P1_34_gpio_pd_pin>; + pinctrl-4 = <&P1_34_pwm_pin>; + }; + + /* P1_35 (ZCZ ball V5) pruin */ + P1_35_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P1_35_default_pin>; + pinctrl-1 = <&P1_35_gpio_pin>; + pinctrl-2 = <&P1_35_gpio_pu_pin>; + pinctrl-3 = <&P1_35_gpio_pd_pin>; + pinctrl-4 = <&P1_35_pruout_pin>; + pinctrl-5 = <&P1_35_pruin_pin>; + }; + + /* P1_36 (ZCZ ball A13) pwm */ + P1_36_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_sclk", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P1_36_default_pin>; + pinctrl-1 = <&P1_36_gpio_pin>; + pinctrl-2 = <&P1_36_gpio_pu_pin>; + pinctrl-3 = <&P1_36_gpio_pd_pin>; + pinctrl-4 = <&P1_36_spi_sclk_pin>; + pinctrl-5 = <&P1_36_pwm_pin>; + pinctrl-6 = <&P1_36_pruout_pin>; + pinctrl-7 = <&P1_36_pruin_pin>; + }; + + + /************************/ + /* P2 Header */ + /************************/ + + /* P2_01 (ZCZ ball U14) pwm */ + P2_01_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P2_01_default_pin>; + pinctrl-1 = <&P2_01_gpio_pin>; + pinctrl-2 = <&P2_01_gpio_pu_pin>; + pinctrl-3 = <&P2_01_gpio_pd_pin>; + pinctrl-4 = <&P2_01_pwm_pin>; + }; + + /* P2_02 (ZCZ ball V17) */ + P2_02_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd"; + pinctrl-0 = <&P2_02_default_pin>; + pinctrl-1 = <&P2_02_gpio_pin>; + pinctrl-2 = <&P2_02_gpio_pu_pin>; + pinctrl-3 = <&P2_02_gpio_pd_pin>; + }; + + /* P2_03 (ZCZ ball T10) */ + P2_03_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P2_03_default_pin>; + pinctrl-1 = <&P2_03_gpio_pin>; + pinctrl-2 = <&P2_03_gpio_pu_pin>; + pinctrl-3 = <&P2_03_gpio_pd_pin>; + pinctrl-4 = <&P2_03_pwm_pin>; + }; + + /* P2_04 (ZCZ ball T16) */ + P2_04_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd"; + pinctrl-0 = <&P2_04_default_pin>; + pinctrl-1 = <&P2_04_gpio_pin>; + pinctrl-2 = <&P2_04_gpio_pu_pin>; + pinctrl-3 = <&P2_04_gpio_pd_pin>; + }; + + /* P2_05 (ZCZ ball T17) uart */ + P2_05_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart"; + pinctrl-0 = <&P2_05_default_pin>; + pinctrl-1 = <&P2_05_gpio_pin>; + pinctrl-2 = <&P2_05_gpio_pu_pin>; + pinctrl-3 = <&P2_05_gpio_pd_pin>; + pinctrl-4 = <&P2_05_uart_pin>; + }; + + /* P2_06 (ZCZ ball U16) */ + P2_06_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd"; + pinctrl-0 = <&P2_06_default_pin>; + pinctrl-1 = <&P2_06_gpio_pin>; + pinctrl-2 = <&P2_06_gpio_pu_pin>; + pinctrl-3 = <&P2_06_gpio_pd_pin>; + }; + + /* P2_07 (ZCZ ball U17) uart */ + P2_07_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart"; + pinctrl-0 = <&P2_07_default_pin>; + pinctrl-1 = <&P2_07_gpio_pin>; + pinctrl-2 = <&P2_07_gpio_pu_pin>; + pinctrl-3 = <&P2_07_gpio_pd_pin>; + pinctrl-4 = <&P2_07_uart_pin>; + }; + + /* P2_08 (ZCZ ball U18) */ + P2_08_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd"; + pinctrl-0 = <&P2_08_default_pin>; + pinctrl-1 = <&P2_08_gpio_pin>; + pinctrl-2 = <&P2_08_gpio_pu_pin>; + pinctrl-3 = <&P2_08_gpio_pd_pin>; + }; + + /* P2_09 (ZCZ ball D15) i2c */ + P2_09_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart", "can", "i2c", "pru_uart", "pruin"; + pinctrl-0 = <&P2_09_default_pin>; + pinctrl-1 = <&P2_09_gpio_pin>; + pinctrl-2 = <&P2_09_gpio_pu_pin>; + pinctrl-3 = <&P2_09_gpio_pd_pin>; + pinctrl-4 = <&P2_09_uart_pin>; + pinctrl-5 = <&P2_09_can_pin>; + pinctrl-6 = <&P2_09_i2c_pin>; + pinctrl-7 = <&P2_09_pru_uart_pin>; + pinctrl-8 = <&P2_09_pruin_pin>; + }; + + /* P2_10 (ZCZ ball R14) */ + P2_10_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep"; + pinctrl-0 = <&P2_10_default_pin>; + pinctrl-1 = <&P2_10_gpio_pin>; + pinctrl-2 = <&P2_10_gpio_pu_pin>; + pinctrl-3 = <&P2_10_gpio_pd_pin>; + pinctrl-4 = <&P2_10_eqep_pin>; + }; + + /* P2_11 (ZCZ ball D16) i2c */ + P2_11_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart", "can", "i2c", "pru_uart", "pruin"; + pinctrl-0 = <&P2_11_default_pin>; + pinctrl-1 = <&P2_11_gpio_pin>; + pinctrl-2 = <&P2_11_gpio_pu_pin>; + pinctrl-3 = <&P2_11_gpio_pd_pin>; + pinctrl-4 = <&P2_11_uart_pin>; + pinctrl-5 = <&P2_11_can_pin>; + pinctrl-6 = <&P2_11_i2c_pin>; + pinctrl-7 = <&P2_11_pru_uart_pin>; + pinctrl-8 = <&P2_11_pruin_pin>; + }; + + /* P2_12 POWER_BUTTON */ + + /* P2_13 VOUT-5V */ + + /* P2_14 BAT-VIN */ + + /* P2_15 GND */ + + /* P2_16 BAT-TEMP */ + + /* P2_17 (ZCZ ball V12) */ + P2_17_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd"; + pinctrl-0 = <&P2_17_default_pin>; + pinctrl-1 = <&P2_17_gpio_pin>; + pinctrl-2 = <&P2_17_gpio_pu_pin>; + pinctrl-3 = <&P2_17_gpio_pd_pin>; + }; + + /* P2_18 (ZCZ ball U13) */ + P2_18_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pru_ecap_pwm", "pruin"; + pinctrl-0 = <&P2_18_default_pin>; + pinctrl-1 = <&P2_18_gpio_pin>; + pinctrl-2 = <&P2_18_gpio_pu_pin>; + pinctrl-3 = <&P2_18_gpio_pd_pin>; + pinctrl-4 = <&P2_18_eqep_pin>; + pinctrl-5 = <&P2_18_pru_ecap_pwm_pin>; + pinctrl-6 = <&P2_18_pruin_pin>; + }; + + /* P2_19 (ZCZ ball U12) */ + P2_19_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P2_19_default_pin>; + pinctrl-1 = <&P2_19_gpio_pin>; + pinctrl-2 = <&P2_19_gpio_pu_pin>; + pinctrl-3 = <&P2_19_gpio_pd_pin>; + pinctrl-4 = <&P2_19_pwm_pin>; + }; + + /* P2_20 (ZCZ ball T13) */ + P2_20_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd"; + pinctrl-0 = <&P2_20_default_pin>; + pinctrl-1 = <&P2_20_gpio_pin>; + pinctrl-2 = <&P2_20_gpio_pu_pin>; + pinctrl-3 = <&P2_20_gpio_pd_pin>; + }; + + /* P2_21 GND */ + + /* P2_22 (ZCZ ball V13) */ + P2_22_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruin"; + pinctrl-0 = <&P2_22_default_pin>; + pinctrl-1 = <&P2_22_gpio_pin>; + pinctrl-2 = <&P2_22_gpio_pu_pin>; + pinctrl-3 = <&P2_22_gpio_pd_pin>; + pinctrl-4 = <&P2_22_eqep_pin>; + pinctrl-5 = <&P2_22_pruin_pin>; + }; + + /* P2_23 VOUT-3.3V */ + + /* P2_24 (ZCZ ball T12) */ + P2_24_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout"; + pinctrl-0 = <&P2_24_default_pin>; + pinctrl-1 = <&P2_24_gpio_pin>; + pinctrl-2 = <&P2_24_gpio_pu_pin>; + pinctrl-3 = <&P2_24_gpio_pd_pin>; + pinctrl-4 = <&P2_24_eqep_pin>; + pinctrl-5 = <&P2_24_pruout_pin>; + }; + + /* P2_25 (ZCZ ball E17) spi */ + P2_25_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi", "spi_cs", "uart", "can", "i2c"; + pinctrl-0 = <&P2_25_default_pin>; + pinctrl-1 = <&P2_25_gpio_pin>; + pinctrl-2 = <&P2_25_gpio_pu_pin>; + pinctrl-3 = <&P2_25_gpio_pd_pin>; + pinctrl-4 = <&P2_25_spi_pin>; + pinctrl-5 = <&P2_25_spi_cs_pin>; + pinctrl-6 = <&P2_25_uart_pin>; + pinctrl-7 = <&P2_25_can_pin>; + pinctrl-8 = <&P2_25_i2c_pin>; + }; + + /* P2_26 RESET# */ + + /* P2_27 (ZCZ ball E18) spi */ + P2_27_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi", "uart", "can", "i2c"; + pinctrl-0 = <&P2_27_default_pin>; + pinctrl-1 = <&P2_27_gpio_pin>; + pinctrl-2 = <&P2_27_gpio_pu_pin>; + pinctrl-3 = <&P2_27_gpio_pd_pin>; + pinctrl-4 = <&P2_27_spi_pin>; + pinctrl-5 = <&P2_27_uart_pin>; + pinctrl-6 = <&P2_27_can_pin>; + pinctrl-7 = <&P2_27_i2c_pin>; + }; + + /* P2_28 (ZCZ ball D13) pruin */ + P2_28_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout", "pruin"; + pinctrl-0 = <&P2_28_default_pin>; + pinctrl-1 = <&P2_28_gpio_pin>; + pinctrl-2 = <&P2_28_gpio_pu_pin>; + pinctrl-3 = <&P2_28_gpio_pd_pin>; + pinctrl-4 = <&P2_28_eqep_pin>; + pinctrl-5 = <&P2_28_pruout_pin>; + pinctrl-6 = <&P2_28_pruin_pin>; + }; + + /* P2_29 (ZCZ ball C18) spi_sclk */ + P2_29_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_cs", "spi_sclk", "uart", "pwm", "pru_ecap_pwm"; + pinctrl-0 = <&P2_29_default_pin>; + pinctrl-1 = <&P2_29_gpio_pin>; + pinctrl-2 = <&P2_29_gpio_pu_pin>; + pinctrl-3 = <&P2_29_gpio_pd_pin>; + pinctrl-4 = <&P2_29_spi_cs_pin>; + pinctrl-5 = <&P2_29_spi_sclk_pin>; + pinctrl-6 = <&P2_29_uart_pin>; + pinctrl-7 = <&P2_29_pwm_pin>; + pinctrl-8 = <&P2_29_pru_ecap_pwm_pin>; + }; + + /* P2_30 (ZCZ ball C12) pruin */ + P2_30_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_cs", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P2_30_default_pin>; + pinctrl-1 = <&P2_30_gpio_pin>; + pinctrl-2 = <&P2_30_gpio_pu_pin>; + pinctrl-3 = <&P2_30_gpio_pd_pin>; + pinctrl-4 = <&P2_30_spi_cs_pin>; + pinctrl-5 = <&P2_30_pwm_pin>; + pinctrl-6 = <&P2_30_pruout_pin>; + pinctrl-7 = <&P2_30_pruin_pin>; + }; + + /* P2_31 (ZCZ ball A15) spi_cs */ + P2_31_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_cs", "pruin"; + pinctrl-0 = <&P2_31_default_pin>; + pinctrl-1 = <&P2_31_gpio_pin>; + pinctrl-2 = <&P2_31_gpio_pu_pin>; + pinctrl-3 = <&P2_31_gpio_pd_pin>; + pinctrl-4 = <&P2_31_spi_cs_pin>; + pinctrl-5 = <&P2_31_pruin_pin>; + }; + + /* P2_32 (ZCZ ball D12) pruin */ + P2_32_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P2_32_default_pin>; + pinctrl-1 = <&P2_32_gpio_pin>; + pinctrl-2 = <&P2_32_gpio_pu_pin>; + pinctrl-3 = <&P2_32_gpio_pd_pin>; + pinctrl-4 = <&P2_32_spi_pin>; + pinctrl-5 = <&P2_32_pwm_pin>; + pinctrl-6 = <&P2_32_pruout_pin>; + pinctrl-7 = <&P2_32_pruin_pin>; + }; + + /* P2_33 (ZCZ ball R12) */ + P2_33_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout"; + pinctrl-0 = <&P2_33_default_pin>; + pinctrl-1 = <&P2_33_gpio_pin>; + pinctrl-2 = <&P2_33_gpio_pu_pin>; + pinctrl-3 = <&P2_33_gpio_pd_pin>; + pinctrl-4 = <&P2_33_eqep_pin>; + pinctrl-5 = <&P2_33_pruout_pin>; + }; + + /* P2_34 (ZCZ ball C13) pruin */ + P2_34_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout", "pruin"; + pinctrl-0 = <&P2_34_default_pin>; + pinctrl-1 = <&P2_34_gpio_pin>; + pinctrl-2 = <&P2_34_gpio_pu_pin>; + pinctrl-3 = <&P2_34_gpio_pd_pin>; + pinctrl-4 = <&P2_34_eqep_pin>; + pinctrl-5 = <&P2_34_pruout_pin>; + pinctrl-6 = <&P2_34_pruin_pin>; + }; + + /* P2_35 (ZCZ ball U5) gpio */ + P2_35_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P2_35_default_pin>; + pinctrl-1 = <&P2_35_gpio_pin>; + pinctrl-2 = <&P2_35_gpio_pu_pin>; + pinctrl-3 = <&P2_35_gpio_pd_pin>; + pinctrl-4 = <&P2_35_pruout_pin>; + pinctrl-5 = <&P2_35_pruin_pin>; + }; + + /* P2_36 (ZCZ ball C9) AIN7 */ + + cape-universal { + compatible = "gpio-of-helper"; + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + + P1_02 { + gpio-name = "P1_02"; + gpio = <&gpio2 23 0>; + input; + dir-changeable; + }; + + P1_04 { + gpio-name = "P1_04"; + gpio = <&gpio2 25 0>; + input; + dir-changeable; + }; + + P1_06 { + gpio-name = "P1_06"; + gpio = <&gpio0 5 0>; + input; + dir-changeable; + }; + + P1_08 { + gpio-name = "P1_08"; + gpio = <&gpio0 2 0>; + input; + dir-changeable; + }; + + P1_10 { + gpio-name = "P1_10"; + gpio = <&gpio0 3 0>; + input; + dir-changeable; + }; + + P1_12 { + gpio-name = "P1_12"; + gpio = <&gpio0 4 0>; + input; + dir-changeable; + }; + + P1_20 { + gpio-name = "P1_20"; + gpio = <&gpio0 20 0>; + input; + dir-changeable; + }; + + P1_26 { + gpio-name = "P1_26"; + gpio = <&gpio0 12 0>; + input; + dir-changeable; + }; + + P1_28 { + gpio-name = "P1_28"; + gpio = <&gpio0 13 0>; + input; + dir-changeable; + }; + + P1_29 { + gpio-name = "P1_29"; + gpio = <&gpio3 21 0>; + input; + dir-changeable; + }; + + P1_30 { + gpio-name = "P1_30"; + gpio = <&gpio1 11 0>; + input; + dir-changeable; + }; + + P1_31 { + gpio-name = "P1_31"; + gpio = <&gpio3 18 0>; + input; + dir-changeable; + }; + + P1_32 { + gpio-name = "P1_32"; + gpio = <&gpio1 10 0>; + input; + dir-changeable; + }; + + P1_33 { + gpio-name = "P1_33"; + gpio = <&gpio3 15 0>; + input; + dir-changeable; + }; + + P1_34 { + gpio-name = "P1_34"; + gpio = <&gpio0 26 0>; + input; + dir-changeable; + }; + + P1_35 { + gpio-name = "P1_35"; + gpio = <&gpio2 24 0>; + input; + dir-changeable; + }; + + P1_36 { + gpio-name = "P1_36"; + gpio = <&gpio3 14 0>; + input; + dir-changeable; + }; + + P2_01 { + gpio-name = "P2_01"; + gpio = <&gpio1 18 0>; + input; + dir-changeable; + }; + + P2_02 { + gpio-name = "P2_02"; + gpio = <&gpio1 27 0>; + input; + dir-changeable; + }; + + P2_03 { + gpio-name = "P2_03"; + gpio = <&gpio0 23 0>; + input; + dir-changeable; + }; + + P2_04 { + gpio-name = "P2_04"; + gpio = <&gpio1 26 0>; + input; + dir-changeable; + }; + + P2_05 { + gpio-name = "P2_05"; + gpio = <&gpio0 30 0>; + input; + dir-changeable; + }; + + P2_06 { + gpio-name = "P2_06"; + gpio = <&gpio1 25 0>; + input; + dir-changeable; + }; + + P2_07 { + gpio-name = "P2_07"; + gpio = <&gpio0 31 0>; + input; + dir-changeable; + }; + + P2_08 { + gpio-name = "P2_08"; + gpio = <&gpio1 28 0>; + input; + dir-changeable; + }; + + P2_09 { + gpio-name = "P2_09"; + gpio = <&gpio0 15 0>; + input; + dir-changeable; + }; + + P2_10 { + gpio-name = "P2_10"; + gpio = <&gpio1 20 0>; + input; + dir-changeable; + }; + + P2_11 { + gpio-name = "P2_11"; + gpio = <&gpio0 14 0>; + input; + dir-changeable; + }; + + P2_17 { + gpio-name = "P2_17"; + gpio = <&gpio2 1 0>; + input; + dir-changeable; + }; + + P2_18 { + gpio-name = "P2_18"; + gpio = <&gpio1 15 0>; + input; + dir-changeable; + }; + + P2_19 { + gpio-name = "P2_19"; + gpio = <&gpio0 27 0>; + input; + dir-changeable; + }; + + P2_20 { + gpio-name = "P2_20"; + gpio = <&gpio2 0 0>; + input; + dir-changeable; + }; + + P2_22 { + gpio-name = "P2_22"; + gpio = <&gpio1 14 0>; + input; + dir-changeable; + }; + + P2_24 { + gpio-name = "P2_24"; + gpio = <&gpio1 12 0>; + input; + dir-changeable; + }; + + P2_25 { + gpio-name = "P2_25"; + gpio = <&gpio1 9 0>; + input; + dir-changeable; + }; + + P2_27 { + gpio-name = "P2_27"; + gpio = <&gpio1 8 0>; + input; + dir-changeable; + }; + + P2_28 { + gpio-name = "P2_28"; + gpio = <&gpio3 20 0>; + input; + dir-changeable; + }; + + P2_29 { + gpio-name = "P2_29"; + gpio = <&gpio0 7 0>; + input; + dir-changeable; + }; + + P2_30 { + gpio-name = "P2_30"; + gpio = <&gpio3 17 0>; + input; + dir-changeable; + }; + + P2_31 { + gpio-name = "P2_31"; + gpio = <&gpio0 19 0>; + input; + dir-changeable; + }; + + P2_32 { + gpio-name = "P2_32"; + gpio = <&gpio3 16 0>; + input; + dir-changeable; + }; + + P2_33 { + gpio-name = "P2_33"; + gpio = <&gpio1 13 0>; + input; + dir-changeable; + }; + + P2_34 { + gpio-name = "P2_34"; + gpio = <&gpio3 19 0>; + input; + dir-changeable; + }; + + P2_35 { + gpio-name = "P2_35"; + gpio = <&gpio2 22 0>; + input; + dir-changeable; + }; + + }; +}; diff --git a/arch/arm/boot/dts/am335x-sancloud-bbe-common.dtsi b/arch/arm/boot/dts/am335x-sancloud-bbe-common.dtsi new file mode 100644 index 0000000000000..21b601fa4c127 --- /dev/null +++ b/arch/arm/boot/dts/am335x-sancloud-bbe-common.dtsi @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +&am33xx_pinmux { + cpsw_default: cpsw_default { + pinctrl-single,pins = < + /* Slave 1 */ + AM33XX_PADCONF(AM335X_PIN_MII1_TX_EN, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mii1_txen.rgmii1_tctl */ + AM33XX_PADCONF(AM335X_PIN_MII1_RX_DV, PIN_INPUT_PULLDOWN, MUX_MODE2) /* mii1_rxdv.rgmii1_rctl */ + AM33XX_PADCONF(AM335X_PIN_MII1_TXD3, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mii1_txd3.rgmii1_td3 */ + AM33XX_PADCONF(AM335X_PIN_MII1_TXD2, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mii1_txd2.rgmii1_td2 */ + AM33XX_PADCONF(AM335X_PIN_MII1_TXD1, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mii1_txd1.rgmii1_td1 */ + AM33XX_PADCONF(AM335X_PIN_MII1_TXD0, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mii1_txd0.rgmii1_td0 */ + AM33XX_PADCONF(AM335X_PIN_MII1_TX_CLK, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mii1_txclk.rgmii1_tclk */ + AM33XX_PADCONF(AM335X_PIN_MII1_RX_CLK, PIN_INPUT_PULLDOWN, MUX_MODE2) /* mii1_rxclk.rgmii1_rclk */ + AM33XX_PADCONF(AM335X_PIN_MII1_RXD3, PIN_INPUT_PULLDOWN, MUX_MODE2) /* mii1_rxd3.rgmii1_rd3 */ + AM33XX_PADCONF(AM335X_PIN_MII1_RXD2, PIN_INPUT_PULLDOWN, MUX_MODE2) /* mii1_rxd2.rgmii1_rd2 */ + AM33XX_PADCONF(AM335X_PIN_MII1_RXD1, PIN_INPUT_PULLDOWN, MUX_MODE2) /* mii1_rxd1.rgmii1_rd1 */ + AM33XX_PADCONF(AM335X_PIN_MII1_RXD0, PIN_INPUT_PULLDOWN, MUX_MODE2) /* mii1_rxd0.rgmii1_rd0 */ + >; + }; + + cpsw_sleep: cpsw_sleep { + pinctrl-single,pins = < + /* Slave 1 reset value */ + AM33XX_PADCONF(AM335X_PIN_MII1_TX_EN, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_RX_DV, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_TXD3, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_TXD2, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_TXD1, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_TXD0, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_TX_CLK, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_RX_CLK, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_RXD3, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_RXD2, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_RXD1, PIN_INPUT_PULLDOWN, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_MII1_RXD0, PIN_INPUT_PULLDOWN, MUX_MODE7) + >; + }; + + usb_hub_ctrl: usb_hub_ctrl { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_RMII1_REF_CLK, PIN_OUTPUT_PULLUP, MUX_MODE7) /* rmii1_refclk.gpio0_29 */ + >; + }; +}; + +&mac { + pinctrl-0 = <&cpsw_default>; + pinctrl-1 = <&cpsw_sleep>; +}; + +&cpsw_emac0 { + phy-mode = "rgmii-id"; +}; + +&i2c0 { + usb2512b: usb-hub@2c { + pinctrl-names = "default"; + pinctrl-0 = <&usb_hub_ctrl>; + compatible = "microchip,usb2512b"; + reg = <0x2c>; + reset-gpios = <&gpio0 29 GPIO_ACTIVE_LOW>; + }; +}; diff --git a/arch/arm/boot/dts/am335x-sancloud-bbe-extended-wifi-uboot-univ.dts b/arch/arm/boot/dts/am335x-sancloud-bbe-extended-wifi-uboot-univ.dts new file mode 100644 index 0000000000000..c58ef7d4ca483 --- /dev/null +++ b/arch/arm/boot/dts/am335x-sancloud-bbe-extended-wifi-uboot-univ.dts @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021 Sancloud Ltd + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am335x-sancloud-bbe-common.dtsi" +#include "am335x-bone-common-univ.dtsi" +#include + +/ { + model = "SanCloud BeagleBone Enhanced Extended WiFi"; + compatible = "sancloud,am335x-boneenhanced", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + chosen { + base_dtb = "am335x-sancloud-bbe-extended-wifi-uboot-univ.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; + + wlan_en_reg: fixedregulator@2 { + compatible = "regulator-fixed"; + regulator-name = "wlan-en-regulator"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + startup-delay-us= <100000>; + }; +}; + +/* + * Free up the pins used by this board from the pinmux helpers. + */ +&ocp { + P8_11_pinmux { status = "disabled"; }; + P8_12_pinmux { status = "disabled"; }; + P8_15_pinmux { status = "disabled"; }; + P8_16_pinmux { status = "disabled"; }; + P8_18_pinmux { status = "disabled"; }; + P9_19_pinmux { status = "disabled"; }; + P9_20_pinmux { status = "disabled"; }; + P9_24_pinmux { status = "disabled"; }; + P9_26_pinmux { status = "disabled"; }; +}; + +&am33xx_pinmux { + mmc3_pins: pinmux_mmc3_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_A9, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gpmc_a9.gpio1_25: RADIO_EN */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD12, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad12.mmc2_dat0 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD13, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad13.mmc2_dat1 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD14, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad14.mmc2_dat2 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD15, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad15.mmc2_dat3 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_CSN3, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_csn3.mmc2_cmd */ + AM33XX_PADCONF(AM335X_PIN_GPMC_CLK, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_clk.mmc2_clk */ + >; + }; + + bluetooth_pins: pinmux_bluetooth_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_INPUT_PULLUP, MUX_MODE7) /* event_intr0.gpio0_19 */ + >; + }; + + uart1_pins: pinmux_uart1_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_UART1_RXD, PIN_INPUT, MUX_MODE0) /* uart1_rxd */ + AM33XX_PADCONF(AM335X_PIN_UART1_TXD, PIN_INPUT, MUX_MODE0) /* uart1_txd */ + AM33XX_PADCONF(AM335X_PIN_UART1_CTSN, PIN_INPUT_PULLDOWN, MUX_MODE0) /* uart1_ctsn */ + AM33XX_PADCONF(AM335X_PIN_UART1_RTSN, PIN_OUTPUT_PULLDOWN, MUX_MODE0) /* uart1_rtsn */ + >; + }; +}; + +&i2c2 { + status = "disabled"; +}; + +&mmc3 { + status = "okay"; + vmmc-supply = <&wlan_en_reg>; + bus-width = <4>; + non-removable; + cap-power-off-card; + ti,needs-special-hs-handling; + keep-power-in-suspend; + pinctrl-names = "default"; + pinctrl-0 = <&mmc3_pins>; + dmas = <&edma_xbar 12 0 1 + &edma_xbar 13 0 2>; + dma-names = "tx", "rx"; + clock-frequency = <50000000>; + max-frequency = <50000000>; +}; + +&uart1 { + status = "okay"; + + bluetooth { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins &bluetooth_pins>; + compatible = "qcom,qca6174-bt"; + enable-gpios = <&gpio1 25 GPIO_ACTIVE_HIGH>; + clocks = <&l4ls_clkctrl AM3_L4LS_UART2_CLKCTRL 0>; + interrupt-parent = <&gpio0>; + interrupts = <19 IRQ_TYPE_EDGE_RISING>; + }; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; diff --git a/arch/arm/boot/dts/am335x-sancloud-bbe-extended-wifi-uboot.dts b/arch/arm/boot/dts/am335x-sancloud-bbe-extended-wifi-uboot.dts new file mode 100644 index 0000000000000..4ec00c568caa6 --- /dev/null +++ b/arch/arm/boot/dts/am335x-sancloud-bbe-extended-wifi-uboot.dts @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021 Sancloud Ltd + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am335x-sancloud-bbe-common.dtsi" +#include + +/ { + model = "SanCloud BeagleBone Enhanced Extended WiFi"; + compatible = "sancloud,am335x-boneenhanced", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + chosen { + base_dtb = "am335x-sancloud-bbe-extended-wifi-uboot.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; + + wlan_en_reg: fixedregulator@2 { + compatible = "regulator-fixed"; + regulator-name = "wlan-en-regulator"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + startup-delay-us= <100000>; + }; +}; + +&am33xx_pinmux { + mmc3_pins: pinmux_mmc3_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_A9, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gpmc_a9.gpio1_25: RADIO_EN */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD12, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad12.mmc2_dat0 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD13, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad13.mmc2_dat1 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD14, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad14.mmc2_dat2 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD15, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad15.mmc2_dat3 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_CSN3, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_csn3.mmc2_cmd */ + AM33XX_PADCONF(AM335X_PIN_GPMC_CLK, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_clk.mmc2_clk */ + >; + }; + + bluetooth_pins: pinmux_bluetooth_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_INPUT_PULLUP, MUX_MODE7) /* event_intr0.gpio0_19 */ + >; + }; + + uart1_pins: pinmux_uart1_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_UART1_RXD, PIN_INPUT, MUX_MODE0) /* uart1_rxd */ + AM33XX_PADCONF(AM335X_PIN_UART1_TXD, PIN_INPUT, MUX_MODE0) /* uart1_txd */ + AM33XX_PADCONF(AM335X_PIN_UART1_CTSN, PIN_INPUT_PULLDOWN, MUX_MODE0) /* uart1_ctsn */ + AM33XX_PADCONF(AM335X_PIN_UART1_RTSN, PIN_OUTPUT_PULLDOWN, MUX_MODE0) /* uart1_rtsn */ + >; + }; +}; + +&i2c2 { + status = "disabled"; +}; + +&mmc3 { + status = "okay"; + vmmc-supply = <&wlan_en_reg>; + bus-width = <4>; + non-removable; + cap-power-off-card; + ti,needs-special-hs-handling; + keep-power-in-suspend; + pinctrl-names = "default"; + pinctrl-0 = <&mmc3_pins>; + dmas = <&edma_xbar 12 0 1 + &edma_xbar 13 0 2>; + dma-names = "tx", "rx"; + clock-frequency = <50000000>; + max-frequency = <50000000>; +}; + +&uart1 { + status = "okay"; + + bluetooth { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins &bluetooth_pins>; + compatible = "qcom,qca6174-bt"; + enable-gpios = <&gpio1 25 GPIO_ACTIVE_HIGH>; + clocks = <&l4ls_clkctrl AM3_L4LS_UART2_CLKCTRL 0>; + interrupt-parent = <&gpio0>; + interrupts = <19 IRQ_TYPE_EDGE_RISING>; + }; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; diff --git a/arch/arm/boot/dts/am335x-sancloud-bbe-extended-wifi.dts b/arch/arm/boot/dts/am335x-sancloud-bbe-extended-wifi.dts new file mode 100644 index 0000000000000..daeb5bfe2c9dd --- /dev/null +++ b/arch/arm/boot/dts/am335x-sancloud-bbe-extended-wifi.dts @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021 Sancloud Ltd + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am335x-boneblack-common.dtsi" +#include "am335x-sancloud-bbe-common.dtsi" +#include + +/ { + model = "SanCloud BeagleBone Enhanced Extended WiFi"; + compatible = "sancloud,am335x-boneenhanced", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + chosen { + base_dtb = "am335x-sancloud-bbe-extended-wifi.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; + + wlan_en_reg: fixedregulator@2 { + compatible = "regulator-fixed"; + regulator-name = "wlan-en-regulator"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + startup-delay-us= <100000>; + }; +}; + +&am33xx_pinmux { + mmc3_pins: pinmux_mmc3_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_A9, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gpmc_a9.gpio1_25: RADIO_EN */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD12, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad12.mmc2_dat0 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD13, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad13.mmc2_dat1 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD14, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad14.mmc2_dat2 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD15, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad15.mmc2_dat3 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_CSN3, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_csn3.mmc2_cmd */ + AM33XX_PADCONF(AM335X_PIN_GPMC_CLK, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_clk.mmc2_clk */ + >; + }; + + bluetooth_pins: pinmux_bluetooth_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_INPUT_PULLUP, MUX_MODE7) /* event_intr0.gpio0_19 */ + >; + }; + + uart1_pins: pinmux_uart1_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_UART1_RXD, PIN_INPUT, MUX_MODE0) /* uart1_rxd */ + AM33XX_PADCONF(AM335X_PIN_UART1_TXD, PIN_INPUT, MUX_MODE0) /* uart1_txd */ + AM33XX_PADCONF(AM335X_PIN_UART1_CTSN, PIN_INPUT_PULLDOWN, MUX_MODE0) /* uart1_ctsn */ + AM33XX_PADCONF(AM335X_PIN_UART1_RTSN, PIN_OUTPUT_PULLDOWN, MUX_MODE0) /* uart1_rtsn */ + >; + }; +}; + +&i2c2 { + status = "disabled"; +}; + +&mmc3 { + status = "okay"; + vmmc-supply = <&wlan_en_reg>; + bus-width = <4>; + non-removable; + cap-power-off-card; + ti,needs-special-hs-handling; + keep-power-in-suspend; + pinctrl-names = "default"; + pinctrl-0 = <&mmc3_pins>; + dmas = <&edma_xbar 12 0 1 + &edma_xbar 13 0 2>; + dma-names = "tx", "rx"; + clock-frequency = <50000000>; + max-frequency = <50000000>; +}; + +&uart1 { + status = "okay"; + + bluetooth { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins &bluetooth_pins>; + compatible = "qcom,qca6174-bt"; + enable-gpios = <&gpio1 25 GPIO_ACTIVE_HIGH>; + clocks = <&l4ls_clkctrl AM3_L4LS_UART2_CLKCTRL 0>; + interrupt-parent = <&gpio0>; + interrupts = <19 IRQ_TYPE_EDGE_RISING>; + }; +}; diff --git a/arch/arm/boot/dts/am335x-sancloud-bbe-lite-uboot-univ.dts b/arch/arm/boot/dts/am335x-sancloud-bbe-lite-uboot-univ.dts new file mode 100644 index 0000000000000..ba0adc3bf4ffe --- /dev/null +++ b/arch/arm/boot/dts/am335x-sancloud-bbe-lite-uboot-univ.dts @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am335x-sancloud-bbe-common.dtsi" +#include "am335x-bone-common-univ.dtsi" +#include + +/ { + model = "SanCloud BeagleBone Enhanced Lite"; + compatible = "sancloud,am335x-boneenhanced", + "ti,am335x-bone-black", + "ti,am335x-bone", + "ti,am33xx"; + + chosen { + base_dtb = "am335x-sancloud-bbe-lite-uboot-univ.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by this board from the pinmux helpers. + */ +&ocp { + P9_17_pinmux { status = "disabled"; }; /* P9_17 (A16) spi0_cs0.spi0_cs0 */ + P9_18_pinmux { status = "disabled"; }; /* P9_18 (B16) spi0_d1.spi0_d1 */ + P9_21_pinmux { status = "disabled"; }; /* P9_21 (B17) spi0_d0.spi0_d0 */ + P9_22_pinmux { status = "disabled"; }; /* P9_22 (A17) spi0_sclk.spi0_sclk */ +}; + +&am33xx_pinmux { + bb_spi0_pins: pinmux_bb_spi0_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_SPI0_SCLK, PIN_INPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_SPI0_D0, PIN_INPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_SPI0_D1, PIN_INPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_SPI0_CS0, PIN_INPUT, MUX_MODE0) + >; + }; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; + +&spi0 { + #address-cells = <1>; + #size-cells = <0>; + + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_spi0_pins>; + + channel@0 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "micron,spi-authenta"; + symlink = "bone/spi/0.0"; + + reg = <0>; + spi-max-frequency = <16000000>; + spi-cpha; + }; +}; diff --git a/arch/arm/boot/dts/am335x-sancloud-bbe-lite-uboot.dts b/arch/arm/boot/dts/am335x-sancloud-bbe-lite-uboot.dts new file mode 100644 index 0000000000000..f9e7411ae2544 --- /dev/null +++ b/arch/arm/boot/dts/am335x-sancloud-bbe-lite-uboot.dts @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2021 SanCloud Ltd + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am335x-sancloud-bbe-common.dtsi" +#include + +/ { + model = "SanCloud BeagleBone Enhanced Lite"; + compatible = "sancloud,am335x-boneenhanced", + "ti,am335x-bone-black", + "ti,am335x-bone", + "ti,am33xx"; + + chosen { + base_dtb = "am335x-sancloud-bbe-lite-uboot.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; +}; + +&am33xx_pinmux { + bb_spi0_pins: pinmux_bb_spi0_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_SPI0_SCLK, PIN_INPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_SPI0_D0, PIN_INPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_SPI0_D1, PIN_INPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_SPI0_CS0, PIN_INPUT, MUX_MODE0) + >; + }; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; + +&spi0 { + #address-cells = <1>; + #size-cells = <0>; + + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_spi0_pins>; + + channel@0 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "micron,spi-authenta"; + symlink = "bone/spi/0.0"; + + reg = <0>; + spi-max-frequency = <16000000>; + spi-cpha; + }; +}; diff --git a/arch/arm/boot/dts/am335x-sancloud-bbe-lite.dts b/arch/arm/boot/dts/am335x-sancloud-bbe-lite.dts new file mode 100644 index 0000000000000..5ba77a20f79f2 --- /dev/null +++ b/arch/arm/boot/dts/am335x-sancloud-bbe-lite.dts @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2021 SanCloud Ltd + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am335x-boneblack-common.dtsi" +#include "am335x-sancloud-bbe-common.dtsi" + +/ { + model = "SanCloud BeagleBone Enhanced Lite"; + compatible = "sancloud,am335x-boneenhanced", + "ti,am335x-bone-black", + "ti,am335x-bone", + "ti,am33xx"; + + chosen { + base_dtb = "am335x-sancloud-bbe-lite.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; +}; + +&am33xx_pinmux { + bb_spi0_pins: pinmux_bb_spi0_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_SPI0_SCLK, PIN_INPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_SPI0_D0, PIN_INPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_SPI0_D1, PIN_INPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_SPI0_CS0, PIN_INPUT, MUX_MODE0) + >; + }; +}; + +&spi0 { + #address-cells = <1>; + #size-cells = <0>; + + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_spi0_pins>; + + channel@0 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "micron,spi-authenta"; + symlink = "bone/spi/0.0"; + + reg = <0>; + spi-max-frequency = <16000000>; + spi-cpha; + }; +}; diff --git a/arch/arm/boot/dts/am335x-sancloud-bbe-uboot-univ.dts b/arch/arm/boot/dts/am335x-sancloud-bbe-uboot-univ.dts new file mode 100644 index 0000000000000..dc13f8d82a3a9 --- /dev/null +++ b/arch/arm/boot/dts/am335x-sancloud-bbe-uboot-univ.dts @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am335x-sancloud-bbe-common.dtsi" +#include "am335x-bone-common-univ.dtsi" +#include + +/ { + model = "SanCloud BeagleBone Enhanced"; + compatible = "sancloud,am335x-boneenhanced", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + chosen { + base_dtb = "am335x-sancloud-bbe-uboot-univ.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; +}; + +&am33xx_pinmux { + mpu6050_pins: pinmux_mpu6050_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_UART0_CTSN, PIN_INPUT, MUX_MODE7) /* uart0_ctsn.gpio1_8 */ + >; + }; + + lps3331ap_pins: pinmux_lps3331ap_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_A10, PIN_INPUT, MUX_MODE7) /* gpmc_a10.gpio1_26 */ + >; + }; +}; + +&i2c0 { + lps331ap: barometer@5c { + pinctrl-names = "default"; + pinctrl-0 = <&lps3331ap_pins>; + compatible = "st,lps331ap-press"; + st,drdy-int-pin = <1>; + reg = <0x5c>; + interrupt-parent = <&gpio1>; + interrupts = <26 IRQ_TYPE_EDGE_RISING>; + }; + + mpu6050: accelerometer@68 { + pinctrl-names = "default"; + pinctrl-0 = <&mpu6050_pins>; + compatible = "invensense,mpu6050"; + reg = <0x68>; + interrupt-parent = <&gpio0>; + interrupts = <2 IRQ_TYPE_EDGE_RISING>; + orientation = <0xff 0 0 0 1 0 0 0 0xff>; + }; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; diff --git a/arch/arm/boot/dts/am335x-sancloud-bbe-uboot.dts b/arch/arm/boot/dts/am335x-sancloud-bbe-uboot.dts new file mode 100644 index 0000000000000..2c1a4c25dc1ce --- /dev/null +++ b/arch/arm/boot/dts/am335x-sancloud-bbe-uboot.dts @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am335x-sancloud-bbe-common.dtsi" +#include + +/ { + model = "SanCloud BeagleBone Enhanced"; + compatible = "sancloud,am335x-boneenhanced", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + chosen { + base_dtb = "am335x-sancloud-bbe-uboot.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; +}; + +&am33xx_pinmux { + mpu6050_pins: pinmux_mpu6050_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_UART0_CTSN, PIN_INPUT, MUX_MODE7) /* uart0_ctsn.gpio1_8 */ + >; + }; + + lps3331ap_pins: pinmux_lps3331ap_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_A10, PIN_INPUT, MUX_MODE7) /* gpmc_a10.gpio1_26 */ + >; + }; +}; + +&i2c0 { + lps331ap: barometer@5c { + pinctrl-names = "default"; + pinctrl-0 = <&lps3331ap_pins>; + compatible = "st,lps331ap-press"; + st,drdy-int-pin = <1>; + reg = <0x5c>; + interrupt-parent = <&gpio1>; + interrupts = <26 IRQ_TYPE_EDGE_RISING>; + }; + + mpu6050: accelerometer@68 { + pinctrl-names = "default"; + pinctrl-0 = <&mpu6050_pins>; + compatible = "invensense,mpu6050"; + reg = <0x68>; + interrupt-parent = <&gpio0>; + interrupts = <2 IRQ_TYPE_EDGE_RISING>; + orientation = <0xff 0 0 0 1 0 0 0 0xff>; + }; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; diff --git a/arch/arm/boot/dts/am335x-sancloud-bbe.dts b/arch/arm/boot/dts/am335x-sancloud-bbe.dts index 86dcd1144db4e..5df4fc464e45a 100644 --- a/arch/arm/boot/dts/am335x-sancloud-bbe.dts +++ b/arch/arm/boot/dts/am335x-sancloud-bbe.dts @@ -7,58 +7,21 @@ #include "am33xx.dtsi" #include "am335x-bone-common.dtsi" #include "am335x-boneblack-common.dtsi" +#include "am335x-boneblack-hdmi.dtsi" +#include "am335x-sancloud-bbe-common.dtsi" #include / { model = "SanCloud BeagleBone Enhanced"; compatible = "sancloud,am335x-boneenhanced", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; -}; - -&am33xx_pinmux { - pinctrl-names = "default"; - cpsw_default: cpsw_default { - pinctrl-single,pins = < - /* Slave 1 */ - AM33XX_PADCONF(AM335X_PIN_MII1_TX_EN, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mii1_txen.rgmii1_tctl */ - AM33XX_PADCONF(AM335X_PIN_MII1_RX_DV, PIN_INPUT_PULLDOWN, MUX_MODE2) /* mii1_rxdv.rgmii1_rctl */ - AM33XX_PADCONF(AM335X_PIN_MII1_TXD3, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mii1_txd3.rgmii1_td3 */ - AM33XX_PADCONF(AM335X_PIN_MII1_TXD2, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mii1_txd2.rgmii1_td2 */ - AM33XX_PADCONF(AM335X_PIN_MII1_TXD1, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mii1_txd1.rgmii1_td1 */ - AM33XX_PADCONF(AM335X_PIN_MII1_TXD0, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mii1_txd0.rgmii1_td0 */ - AM33XX_PADCONF(AM335X_PIN_MII1_TX_CLK, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mii1_txclk.rgmii1_tclk */ - AM33XX_PADCONF(AM335X_PIN_MII1_RX_CLK, PIN_INPUT_PULLDOWN, MUX_MODE2) /* mii1_rxclk.rgmii1_rclk */ - AM33XX_PADCONF(AM335X_PIN_MII1_RXD3, PIN_INPUT_PULLDOWN, MUX_MODE2) /* mii1_rxd3.rgmii1_rd3 */ - AM33XX_PADCONF(AM335X_PIN_MII1_RXD2, PIN_INPUT_PULLDOWN, MUX_MODE2) /* mii1_rxd2.rgmii1_rd2 */ - AM33XX_PADCONF(AM335X_PIN_MII1_RXD1, PIN_INPUT_PULLDOWN, MUX_MODE2) /* mii1_rxd1.rgmii1_rd1 */ - AM33XX_PADCONF(AM335X_PIN_MII1_RXD0, PIN_INPUT_PULLDOWN, MUX_MODE2) /* mii1_rxd0.rgmii1_rd0 */ - >; - }; - - cpsw_sleep: cpsw_sleep { - pinctrl-single,pins = < - /* Slave 1 reset value */ - AM33XX_PADCONF(AM335X_PIN_MII1_TX_EN, PIN_INPUT_PULLDOWN, MUX_MODE7) - AM33XX_PADCONF(AM335X_PIN_MII1_RX_DV, PIN_INPUT_PULLDOWN, MUX_MODE7) - AM33XX_PADCONF(AM335X_PIN_MII1_TXD3, PIN_INPUT_PULLDOWN, MUX_MODE7) - AM33XX_PADCONF(AM335X_PIN_MII1_TXD2, PIN_INPUT_PULLDOWN, MUX_MODE7) - AM33XX_PADCONF(AM335X_PIN_MII1_TXD1, PIN_INPUT_PULLDOWN, MUX_MODE7) - AM33XX_PADCONF(AM335X_PIN_MII1_TXD0, PIN_INPUT_PULLDOWN, MUX_MODE7) - AM33XX_PADCONF(AM335X_PIN_MII1_TX_CLK, PIN_INPUT_PULLDOWN, MUX_MODE7) - AM33XX_PADCONF(AM335X_PIN_MII1_RX_CLK, PIN_INPUT_PULLDOWN, MUX_MODE7) - AM33XX_PADCONF(AM335X_PIN_MII1_RXD3, PIN_INPUT_PULLDOWN, MUX_MODE7) - AM33XX_PADCONF(AM335X_PIN_MII1_RXD2, PIN_INPUT_PULLDOWN, MUX_MODE7) - AM33XX_PADCONF(AM335X_PIN_MII1_RXD1, PIN_INPUT_PULLDOWN, MUX_MODE7) - AM33XX_PADCONF(AM335X_PIN_MII1_RXD0, PIN_INPUT_PULLDOWN, MUX_MODE7) - >; - }; - - usb_hub_ctrl: usb_hub_ctrl { - pinctrl-single,pins = < - AM33XX_PADCONF(AM335X_PIN_RMII1_REF_CLK, PIN_OUTPUT_PULLUP, MUX_MODE7) /* rmii1_refclk.gpio0_29 */ - >; + chosen { + base_dtb = "am335x-sancloud-bbe.dts"; + base_dtb_timestamp = __TIMESTAMP__; }; +}; +&am33xx_pinmux { mpu6050_pins: pinmux_mpu6050_pins { pinctrl-single,pins = < AM33XX_PADCONF(AM335X_PIN_UART0_CTSN, PIN_INPUT, MUX_MODE7) /* uart0_ctsn.gpio1_8 */ @@ -72,18 +35,10 @@ }; }; -&mac_sw { - pinctrl-0 = <&cpsw_default>; - pinctrl-1 = <&cpsw_sleep>; -}; - -&cpsw_port1 { - phy-handle = <ðphy0>; - phy-mode = "rgmii-id"; -}; - &i2c0 { lps331ap: barometer@5c { + pinctrl-names = "default"; + pinctrl-0 = <&lps3331ap_pins>; compatible = "st,lps331ap-press"; st,drdy-int-pin = <1>; reg = <0x5c>; @@ -92,17 +47,12 @@ }; mpu6050: accelerometer@68 { + pinctrl-names = "default"; + pinctrl-0 = <&mpu6050_pins>; compatible = "invensense,mpu6050"; reg = <0x68>; interrupt-parent = <&gpio0>; interrupts = <2 IRQ_TYPE_EDGE_RISING>; orientation = <0xff 0 0 0 1 0 0 0 0xff>; }; - - usb2512b: usb-hub@2c { - compatible = "microchip,usb2512b"; - reg = <0x2c>; - reset-gpios = <&gpio0 29 GPIO_ACTIVE_LOW>; - /* wifi on port 4 */ - }; }; diff --git a/arch/arm/boot/dts/am33xx-l4.dtsi b/arch/arm/boot/dts/am33xx-l4.dtsi index f560cf8d435cb..2d16b2d9c86b7 100644 --- a/arch/arm/boot/dts/am33xx-l4.dtsi +++ b/arch/arm/boot/dts/am33xx-l4.dtsi @@ -252,22 +252,22 @@ ranges = <0x00000000 0x0000d000 0x00001000>, <0x00001000 0x0000e000 0x00001000>; - tscadc: tscadc@0 { - compatible = "ti,am3359-tscadc"; - reg = <0x0 0x1000>; - interrupts = <16>; - status = "disabled"; - dmas = <&edma 53 0>, <&edma 57 0>; - dma-names = "fifo0", "fifo1"; + tscadc: tscadc@0 { + compatible = "ti,am3359-tscadc"; + reg = <0x0 0x1000>; + interrupts = <16>; + status = "disabled"; + dmas = <&edma 53 0>, <&edma 57 0>; + dma-names = "fifo0", "fifo1"; - tsc { - compatible = "ti,am3359-tsc"; - }; - am335x_adc: adc { - #io-channel-cells = <1>; - compatible = "ti,am3359-adc"; - }; + tsc { + compatible = "ti,am3359-tsc"; + }; + am335x_adc: adc { + #io-channel-cells = <1>; + compatible = "ti,am3359-adc"; }; + }; }; target-module@10000 { /* 0x44e10000, ap 22 0c.0 */ @@ -290,7 +290,7 @@ am33xx_pinmux: pinmux@800 { compatible = "pinctrl-single"; reg = <0x800 0x238>; - #pinctrl-cells = <2>; + #pinctrl-cells = <1>; pinctrl-single,register-width = <32>; pinctrl-single,function-mask = <0x7f>; }; @@ -838,7 +838,7 @@ #address-cells = <1>; #size-cells = <1>; ranges = <0x0 0x300000 0x80000>; - status = "disabled"; + status = "okay"; pruss: pruss@0 { compatible = "ti,am3356-pruss"; diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi index d29cb4ec3fd22..7f3ff48eb2777 100644 --- a/arch/arm/boot/dts/am33xx.dtsi +++ b/arch/arm/boot/dts/am33xx.dtsi @@ -588,6 +588,10 @@ #size-cells = <1>; ranges = <0 0x56000000 0x1000000>; + /* + * Closed source PowerVR driver, no child device + * binding or driver in mainline + */ gpu: gpu@0 { compatible = "ti,am3352-sgx530", "img,sgx530"; reg = <0x0 0x10000>; diff --git a/arch/arm/boot/dts/am5729-beagleboneai.dts b/arch/arm/boot/dts/am5729-beagleboneai.dts index 149cfafb90bfc..b60eb71e2b677 100644 --- a/arch/arm/boot/dts/am5729-beagleboneai.dts +++ b/arch/arm/boot/dts/am5729-beagleboneai.dts @@ -5,13 +5,14 @@ /dts-v1/; -#include "dra74x.dtsi" +#include "am5728.dtsi" #include "am57xx-commercial-grade.dtsi" #include "dra74x-mmc-iodelay.dtsi" #include "dra74-ipu-dsp-common.dtsi" #include #include #include +#include "bbai-bone-buses.dtsi" / { model = "BeagleBoard.org BeagleBone AI"; @@ -22,10 +23,19 @@ rtc0 = &tps659038_rtc; rtc1 = &rtc; display0 = &hdmi_conn; + mmc0 = &mmc1; + mmc1 = &mmc2; + mmc2 = &mmc4; + i2c0 = &i2c1; + i2c1 = &i2c5; + i2c2 = &i2c4; + i2c3 = &i2c3; }; chosen { stdout-path = &uart1; + base_dtb = "am5729-beagleboneai.dts"; + base_dtb_timestamp = __TIMESTAMP__; }; memory@0 { @@ -103,6 +113,8 @@ leds { compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&led_pins_default>; led0 { label = "beaglebone:green:usr0"; @@ -186,10 +198,14 @@ emmc_pwrseq: emmc_pwrseq { compatible = "mmc-pwrseq-emmc"; reset-gpios = <&gpio5 7 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&emmc_pwrseq_pins_default>; }; brcmf_pwrseq: brcmf_pwrseq { compatible = "mmc-pwrseq-simple"; + pinctrl-names = "default"; + pinctrl-0 = <&brcmf_pwrseq_pins_default>; reset-gpios = <&gpio3 22 GPIO_ACTIVE_LOW>, /* BT-REG-ON */ <&gpio3 18 GPIO_ACTIVE_LOW>; /* WL-REG-ON */ }; @@ -198,12 +214,84 @@ compatible = "linux,extcon-usb-gpio"; ti,enable-id-detection; id-gpio = <&gpio3 13 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&extcon_usb1_pins_default>; + }; +}; + +&dra7_pmx_core { + extcon_usb1_pins_default: extcon_usb1_pins_default { + pinctrl-single,pins = < + DRA7XX_CORE_IOPAD(0x3518, PIN_INPUT | MUX_MODE14) /* AG2: vin1a_d9.gpio3_13 - USR0 */ + >; + }; + + led_pins_default: led_pins_default { + pinctrl-single,pins = < + DRA7XX_CORE_IOPAD(0x3528, PIN_OUTPUT | MUX_MODE14) /* AF6: vin1a_d13.gpio3_17 - USR0 */ + DRA7XX_CORE_IOPAD(0x36c0, PIN_OUTPUT | MUX_MODE14) /* J11: mcasp1_axr3.gpio5_5 - USR1 */ + DRA7XX_CORE_IOPAD(0x3520, PIN_OUTPUT | MUX_MODE14) /* AG5: vin1a_d12.gpio3_15 - USR2 */ + DRA7XX_CORE_IOPAD(0x351c, PIN_OUTPUT | MUX_MODE14) /* AG3: vin1a_d10.gpio3_14 - USR3 */ + DRA7XX_CORE_IOPAD(0x3500, PIN_OUTPUT | MUX_MODE14) /* AH6: vin1a_d3.gpio3_7 - USR4 */ + >; + }; + + emmc_pwrseq_pins_default: emmc_pwrseq_pins_default { + pinctrl-single,pins = < + DRA7XX_CORE_IOPAD(0x36c8, PIN_OUTPUT_PULLUP | MUX_MODE14) /* F13: mcasp1_axr5.gpio5_7 - eMMC_RSTn */ + >; + }; + + brcmf_pwrseq_pins_default: brcmf_pwrseq_pins_default { + pinctrl-single,pins = < + DRA7XX_CORE_IOPAD(0x352c, PIN_OUTPUT_PULLUP | MUX_MODE14) /* AF3: vin1a_d14.gpio3_18 - WL_REG_ON */ + DRA7XX_CORE_IOPAD(0x353c, PIN_OUTPUT_PULLUP | MUX_MODE14) /* AE5: vin1a_d18.gpio3_22 - BT_REG_ON */ + >; + }; + + wifibt_extra_pins_default: wifibt_extra_pins_default { + pinctrl-single,pins = < + DRA7XX_CORE_IOPAD(0x3540, PIN_INPUT | MUX_MODE14) /* AE1: vin1a_d19.gpio3_23 - WL_HOST_WAKE */ + DRA7XX_CORE_IOPAD(0x3450, PIN_INPUT | MUX_MODE8) /* P6: vin1a_d20.uart6_rxd - UART6_RXD */ + DRA7XX_CORE_IOPAD(0x3454, PIN_INPUT | MUX_MODE8) /* R9: vin1a_d21.uart6_txd - UART6_TXD */ + DRA7XX_CORE_IOPAD(0x3458, PIN_INPUT | MUX_MODE8) /* R5: vin1a_d22.uart6_ctsn - UART6_CTSN */ + DRA7XX_CORE_IOPAD(0x345c, PIN_INPUT | MUX_MODE8) /* P5: vin1a_d23.uart6_rtsn - UART6_RTSN */ + DRA7XX_CORE_IOPAD(0x3534, PIN_INPUT_PULLDOWN | MUX_MODE14) /* AF1: vin1a_d16.gpio3_20 - BT_HOST_WAKE */ + DRA7XX_CORE_IOPAD(0x3538, PIN_OUTPUT_PULLDOWN | MUX_MODE14) /* AE3: vin1a_d6.gpio3_21 - BT_WAKE */ + >; + }; + + adc_pins_default: adc_pins_default { + pinctrl-single,pins = < + DRA7XX_CORE_IOPAD(0x3550, PIN_OUTPUT | MUX_MODE14) /* AD3: vin1a_d23.gpio3_27 - VDD_ADC_SEL */ + DRA7XX_CORE_IOPAD(0x34DC, PIN_INPUT_PULLUP | MUX_MODE14) /* AG8: vin1a_clk0.gpio2_30 - INT_ADC */ + >; + }; + + pmic_pins_default: pmic_pins_default { + pinctrl-single,pins = < + DRA7XX_CORE_IOPAD(0x3690, PIN_INPUT_PULLUP | MUX_MODE14) /* F21: gpio6_16.gpio6_16 - PMIC_INT */ + >; + }; + + hdmi_pins_default: hdmi_pins_default { + pinctrl-single,pins = < + DRA7XX_CORE_IOPAD(0x3808, PIN_INPUT | MUX_MODE1) /* C25: i2c2_sda.hdmi1_ddc_scl - HDMI_DDC_SCL */ + DRA7XX_CORE_IOPAD(0x380C, PIN_INPUT | MUX_MODE1) /* F17: i2c2_scl.hdmi1_ddc_sda - HDMI_DDC_SDA */ + DRA7XX_CORE_IOPAD(0x37BC, PIN_INPUT | MUX_MODE6) /* B20: spi1_cs3.hdmi1_cec - HDMI_DDC_CEC */ +#if 0 + DRA7XX_CORE_IOPAD(0x37B8, PIN_INPUT | MUX_MODE6) /* B21: spi1_cs2.hdmi1_hpd - HDMI_DDC_HPD */ +#else + DRA7XX_CORE_IOPAD(0x37B8, PIN_INPUT | MUX_MODE14) /* B21: spi1_cs2.gpio7_12 - HDMI_DDC_HPD */ +#endif + >; }; }; &i2c1 { status = "okay"; clock-frequency = <400000>; + symlink = "bone/i2c/0"; tps659038: tps659038@58 { compatible = "ti,tps659038"; @@ -211,6 +299,9 @@ interrupt-parent = <&gpio6>; interrupts = <16 IRQ_TYPE_LEVEL_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&pmic_pins_default>; + #interrupt-cells = <2>; interrupt-controller; @@ -424,12 +515,15 @@ st,adc-freq = <1>; /* 3.25 MHz ADC clock speed */ st,sample-time = <4>; /* ADC converstion time: 80 clocks */ + pinctrl-names = "default"; + pinctrl-0 = <&adc_pins_default>; + stmpe_adc { compatible = "st,stmpe-adc"; st,norequest-mask = <0x00>; /* mask any channels to be used by touchscreen */ adc0: iio-device@0 { #io-channel-cells = <1>; - iio-channels = <&adc0 4>, <&adc0 1>, <&adc0 2>, <&adc0 3>, <&adc0 4>, <&adc0 5>, <&adc0 6>; + iio-channels = <&adc0 0>, <&adc0 1>, <&adc0 2>, <&adc0 3>, <&adc0 4>, <&adc0 5>, <&adc0 6>; iio-channel-names = "AIN0_P9_39", "AIN1_P9_40", "AIN2_P9_37", "AIN3_P9_38", "AIN4_P9_33", "AIN5_P9_36", "AIN6_P9_35"; }; @@ -462,6 +556,11 @@ #pwm-cells = <2>; }; }; + + eeprom: eeprom@50 { + compatible = "atmel,24c32"; + reg = <0x50>; + }; }; &mcspi3 { @@ -486,6 +585,7 @@ &uart1 { status = "okay"; + symlink = "bone/uart/0"; }; &davinci_mdio_sw { @@ -550,7 +650,11 @@ ti,needs-special-reset; dmas = <&sdma_xbar 47>, <&sdma_xbar 48>; dma-names = "tx", "rx"; - + pinctrl-names = "default", "hs", "ddr_1_8v", "hs200_1_8v"; + pinctrl-0 = <&mmc2_pins_default>; + pinctrl-1 = <&mmc2_pins_hs>; + pinctrl-2 = <&mmc2_pins_ddr_rev20>; + pinctrl-3 = <&mmc2_pins_hs200>; }; &mmc4 { @@ -563,6 +667,10 @@ /* DDR50: DDR up to 50 MHz (1.8 V signaling). */ status = "okay"; + pinctrl-names = "default", "hs"; + pinctrl-0 = <&mmc4_pins_default &wifibt_extra_pins_default>; + pinctrl-1 = <&mmc4_pins_hs &wifibt_extra_pins_default>; + ti,needs-special-reset; vmmc-supply = <&vdd_3v3>; cap-power-off-card; @@ -614,6 +722,10 @@ dr_mode = "host"; }; +&bb2d { + status = "okay"; +}; + &dss { status = "okay"; vdda_video-supply = <&vdd_1v8_pll>; @@ -622,6 +734,8 @@ &hdmi { status = "okay"; vdda-supply = <&vdd_1v8_phy_ldo4>; + pinctrl-names = "default"; + pinctrl-0 = <&hdmi_pins_default>; port { hdmi_out: endpoint { @@ -675,6 +789,7 @@ &i2c4 { status = "okay"; clock-frequency = <100000>; + symlink = "bone/i2c/2"; }; &cpu0_opp_table { diff --git a/arch/arm/boot/dts/am572x-bone-common-univ.dtsi b/arch/arm/boot/dts/am572x-bone-common-univ.dtsi new file mode 100644 index 0000000000000..574e194dd1c35 --- /dev/null +++ b/arch/arm/boot/dts/am572x-bone-common-univ.dtsi @@ -0,0 +1,2236 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020 Deepak Khatri + */ + +#include + +&dra7_pmx_core { + +/* macro: BONE_PIN( , , */ +#define BONE_PIN(XX,ZZ,QQ) \ + XX##_##ZZ##_pin: pinmux_##XX##_##ZZ##_pin { pinctrl-single,pins = < QQ >; }; + + /************************/ + /* P8 Header */ + /************************/ + + /* P8_01 GND */ + + /* P8_02 GND */ + + + /* P8_03 (ball AB8) gpio1_24 [page 49] */ + BONE_PIN(P8_03, default, P8_03( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* mmc3_dat6.gpio1_24 */ + BONE_PIN(P8_03, gpio, P8_03( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* mmc3_dat6.gpio1_24 */ + BONE_PIN(P8_03, gpio_pu, P8_03( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* mmc3_dat6.gpio1_24 */ + BONE_PIN(P8_03, gpio_pd, P8_03( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* mmc3_dat6.gpio1_24 */ + BONE_PIN(P8_03, pruin, P8_03( PIN_INPUT | MUX_MODE12 )) /* mmc3_dat6.pr2_pru0_gpi10 */ + BONE_PIN(P8_03, pruout, P8_03( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* mmc3_dat6.pr2_pru0_gpo10 */ + + /* P8_04 (ball AB5) gpio1_25 */ + BONE_PIN(P8_04, default, P8_04( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* mmc3_dat7.gpio1_25 */ + BONE_PIN(P8_04, gpio, P8_04( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* mmc3_dat7.gpio1_25 */ + BONE_PIN(P8_04, gpio_pu, P8_04( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* mmc3_dat7.gpio1_25 */ + BONE_PIN(P8_04, gpio_pd, P8_04( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* mmc3_dat7.gpio1_25 */ + BONE_PIN(P8_04, pruin, P8_04( PIN_INPUT | MUX_MODE12 )) /* mmc3_dat7.pr2_pru0_gpi11 */ + BONE_PIN(P8_04, pruout, P8_04( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* mmc3_dat7.pr2_pru0_gpo11 */ + BONE_PIN(P8_04, ecap_pwm, P8_04( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE10 )) /* mmc3_dat7.eCAP3_in_PWM3_out */ + + /* P8_05 (ball AC9) gpio7_1 */ + BONE_PIN(P8_05, default, P8_05( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* mmc3_dat2.gpio7_1 */ + BONE_PIN(P8_05, gpio, P8_05( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* mmc3_dat2.gpio7_1 */ + BONE_PIN(P8_05, gpio_pu, P8_05( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* mmc3_dat2.gpio7_1 */ + BONE_PIN(P8_05, gpio_pd, P8_05( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* mmc3_dat2.gpio7_1 */ + BONE_PIN(P8_05, pruin, P8_05( PIN_INPUT | MUX_MODE12 )) /* mmc3_dat2.pr2_pru0_gpi06 */ + BONE_PIN(P8_05, pruout, P8_05( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* mmc3_dat2.pr2_pru0_gpo06 */ + BONE_PIN(P8_05, eqep, P8_05( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE10 )) /* mmc3_dat2.eQEP3_index */ + + /* P8_06 (ball AC3) gpio7_2 */ + BONE_PIN(P8_06, default, P8_06( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* mmc3_dat3.gpio7_2 */ + BONE_PIN(P8_06, gpio, P8_06( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* mmc3_dat3.gpio7_2 */ + BONE_PIN(P8_06, gpio_pu, P8_06( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* mmc3_dat3.gpio7_2 */ + BONE_PIN(P8_06, gpio_pd, P8_06( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* mmc3_dat3.gpio7_2 */ + BONE_PIN(P8_06, pruin, P8_06( PIN_INPUT | MUX_MODE12 )) /* mmc3_dat3.pr2_pru0_gpi07 */ + BONE_PIN(P8_06, pruout, P8_06( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* mmc3_dat3.pr2_pru0_gpo07 */ + BONE_PIN(P8_06, eqep, P8_06( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE10 )) /* mmc3_dat3.eQEP3_strobe */ + + /* P8_07 (ball G14) gpio6_5*/ + BONE_PIN(P8_07, default, P8_07( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* mcasp1_axr14.gpio6_5 */ + BONE_PIN(P8_07, gpio, P8_07( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* mcasp1_axr14.gpio6_5 */ + BONE_PIN(P8_07, gpio_pu, P8_07( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* mcasp1_axr14.gpio6_5 */ + BONE_PIN(P8_07, gpio_pd, P8_07( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* mcasp1_axr14.gpio6_5 */ + BONE_PIN(P8_07, timer, P8_07( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE10 )) /* mcasp1_axr14.timer11 */ + BONE_PIN(P8_07, pruin, P8_07( PIN_INPUT | MUX_MODE12 )) /* mcasp1_axr14.pr2_pru1_gpi16 */ + BONE_PIN(P8_07, pruout, P8_07( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* mcasp1_axr14.pr2_pru1_gpo16 */ + + /* P8_08 (ball F14) gpio6_6 */ + BONE_PIN(P8_08, default, P8_08( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* mcasp1_axr15.gpio6_6 */ + BONE_PIN(P8_08, gpio, P8_08( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* mcasp1_axr15.gpio6_6 */ + BONE_PIN(P8_08, gpio_pu, P8_08( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* mcasp1_axr15.gpio6_6 */ + BONE_PIN(P8_08, gpio_pd, P8_08( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* mcasp1_axr15.gpio6_6 */ + BONE_PIN(P8_08, timer, P8_08( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE10 )) /* mcasp1_axr15.timer12 */ + BONE_PIN(P8_08, pruin, P8_08( PIN_INPUT | MUX_MODE12 )) /* mcasp1_axr15.pr2_pru0_gpi20 */ + BONE_PIN(P8_08, pruout, P8_08( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* mcasp1_axr15.pr2_pru0_gpo20 */ + + /* P8_09 (ball E17) gpio6_18 */ + BONE_PIN(P8_09, default, P8_09( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* xref_clk1.gpio6_18 */ + BONE_PIN(P8_09, gpio, P8_09( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* xref_clk1.gpio6_18 */ + BONE_PIN(P8_09, gpio_pu, P8_09( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* xref_clk1.gpio6_18 */ + BONE_PIN(P8_09, gpio_pd, P8_09( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* xref_clk1.gpio6_18 */ + BONE_PIN(P8_09, timer, P8_09( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE10 )) /* xref_clk1.timer14 */ + BONE_PIN(P8_09, pruin, P8_09( PIN_INPUT | MUX_MODE12 )) /* xref_clk1.pr2_pru1_gpi06 */ + BONE_PIN(P8_09, pruout, P8_09( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* xref_clk1.pr2_pru1_gpo06 */ + + /* P8_10 (ball A13) gpio6_4 */ + BONE_PIN(P8_10, default, P8_10( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* mcasp1_axr13.gpio6_4 */ + BONE_PIN(P8_10, gpio, P8_10( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* mcasp1_axr13.gpio6_4 */ + BONE_PIN(P8_10, gpio_pu, P8_10( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* mcasp1_axr13.gpio6_4 */ + BONE_PIN(P8_10, gpio_pd, P8_10( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* mcasp1_axr13.gpio6_4 */ + BONE_PIN(P8_10, timer, P8_10( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE10 )) /* mcasp1_axr13.timer10 */ + BONE_PIN(P8_10, pruin, P8_10( PIN_INPUT | MUX_MODE12 )) /* mcasp1_axr13.pr2_pru1_gpi15 */ + BONE_PIN(P8_10, pruout, P8_10( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* mcasp1_axr13.pr2_pru1_gpo15 */ + + /* P8_11 (ball AH4) gpio3_11 */ + BONE_PIN(P8_11, default, P8_11( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vin1a_d7.gpio3_11 */ + BONE_PIN(P8_11, gpio, P8_11( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* vin1a_d7.gpio3_11 */ + BONE_PIN(P8_11, gpio_pu, P8_11( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* vin1a_d7.gpio3_11 */ + BONE_PIN(P8_11, gpio_pd, P8_11( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vin1a_d7.gpio3_11 */ + BONE_PIN(P8_11, eqep, P8_11( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE10 )) /* vin1a_d7.eQEP2B_in */ + BONE_PIN(P8_11, pruin, P8_11( PIN_INPUT | MUX_MODE12 )) /* vin1a_d7.pr1_pru0_gpi4 */ + BONE_PIN(P8_11, pruout, P8_11( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* vin1a_d7.pr1_pru0_gpo4 */ + + /* P8_12 (ball AG6) gpio3_10 */ + BONE_PIN(P8_12, default, P8_12( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vin1a_d6.gpio3_10 */ + BONE_PIN(P8_12, gpio, P8_12( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* vin1a_d6.gpio3_10 */ + BONE_PIN(P8_12, gpio_pu, P8_12( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* vin1a_d6.gpio3_10 */ + BONE_PIN(P8_12, gpio_pd, P8_12( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vin1a_d6.gpio3_10 */ + BONE_PIN(P8_12, eqep, P8_12( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE10 )) /* vin1a_d6.eQEP2A_in */ + BONE_PIN(P8_12, pruin, P8_12( PIN_INPUT | MUX_MODE12 )) /* vin1a_d6.pr1_pru0_gpi3 */ + BONE_PIN(P8_12, pruout, P8_12( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* vin1a_d6.pr1_pru0_gpo3 */ + + /* P8_13 (ball D3) gpio4_11 */ + BONE_PIN(P8_13, default, P8_13( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vin2a_d10.gpio4_11 */ + BONE_PIN(P8_13, gpio, P8_13( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* vin2a_d10.gpio4_11 */ + BONE_PIN(P8_13, gpio_pu, P8_13( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* vin2a_d10.gpio4_11 */ + BONE_PIN(P8_13, gpio_pd, P8_13( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vin2a_d10.gpio4_11 */ + BONE_PIN(P8_13, pwm, P8_13( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE10 )) /* vin2a_d10.ehrpwm2B */ + BONE_PIN(P8_13, pruin, P8_13( PIN_INPUT | MUX_MODE12 )) /* vin2a_d10.pr1_pru1_gpi7 */ + BONE_PIN(P8_13, pruout, P8_13( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* vin2a_d10.pr1_pru1_gpo7 */ + + /* P8_14 (ball D5) gpio4_13*/ + BONE_PIN(P8_14, default, P8_14( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vin2a_d12.gpio4_13 */ + BONE_PIN(P8_14, gpio, P8_14( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* vin2a_d12.gpio4_13 */ + BONE_PIN(P8_14, gpio_pu, P8_14( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* vin2a_d12.gpio4_13 */ + BONE_PIN(P8_14, gpio_pd, P8_14( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vin2a_d12.gpio4_13 */ + BONE_PIN(P8_14, ecap_pwm, P8_14( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE10 )) /* vin2a_d12.eCAP2_in_PWM2_out */ + BONE_PIN(P8_14, pruin, P8_14( PIN_INPUT | MUX_MODE12 )) /* vin2a_d12.pr1_pru1_gpi9 */ + BONE_PIN(P8_14, pruout, P8_14( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* vin2a_d12.pr1_pru1_gpo9 */ + + /* P8_15a (ball D1) gpio4_3*/ + P8_15_default_pin: pinmux_P8_15_default_pin { pinctrl-single,pins = < P8_15A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_15B( PIN_OUTPUT | MUX_MODE15) >; }; /* vin2a_d2.gpio4_3, vin2a_d19.off */ + P8_15_gpio_pin: pinmux_P8_15_gpio_pin { pinctrl-single,pins = < P8_15A( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P8_15B( PIN_OUTPUT | MUX_MODE15) >; }; /* vin2a_d2.gpio4_3, vin2a_d19.off */ + P8_15_gpio_pu_pin: pinmux_P8_15_gpio_pu_pin { pinctrl-single,pins = < P8_15A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P8_15B( PIN_OUTPUT | MUX_MODE15) >; }; /* vin2a_d2.gpio4_3, vin2a_d19.off */ + P8_15_gpio_pd_pin: pinmux_P8_15_gpio_pd_pin { pinctrl-single,pins = < P8_15A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_15B( PIN_OUTPUT | MUX_MODE15) >; }; /* vin2a_d2.gpio4_3, vin2a_d19.off */ + P8_15_pru_ecap_pwm_pin: pinmux_P8_15_pru_ecap_pwm_pin { pinctrl-single,pins = < P8_15A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE11) P8_15B( PIN_OUTPUT | MUX_MODE15) >; }; /* vin2a_d2.pr1_ecap0_ecap_capin_apwm_o, vin2a_d19.off */ + P8_15_ecap_pwm_pin: pinmux_P8_15_ecap_pwm_pin { pinctrl-single,pins = < P8_15A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE10) P8_15B( PIN_OUTPUT | MUX_MODE15) >; }; /* vin2a_d2.eCAP1_in_PWM1_out, vin2a_d19.off */ + + /* P8_15b (ball A3) gpio4_27 */ + P8_15_pruin_pin: pinmux_P8_15_pruin_pin { pinctrl-single,pins = < P8_15B( PIN_INPUT | MUX_MODE12) P8_15A( PIN_OUTPUT | MUX_MODE15) >; }; /* vin2a_d19.pr1_pru1_gpi16, vin2a_d2.off */ + P8_15_pruout_pin: pinmux_P8_15_pruout_pin { pinctrl-single,pins = < P8_15B( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13) P8_15A( PIN_OUTPUT | MUX_MODE15) >; }; /* vin2a_d19.pr1_pru1_gp016, vin2a_d2.off */ + + /* P8_15 dummy nodes for unavailable bone bus pins*/ + P8_15_eqep_pin: pinmux_P8_15_eqep_pin { pinctrl-single,pins = < /* To avoid FDT_ERROR on BBAI when using BONE-EQEP2.dtbo */ + /* Default pinmux configuration of P8_15 (P8_15_default_pin) */ + P8_15A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_15B( PIN_OUTPUT | MUX_MODE15) >; }; /* vin2a_d2.gpio4_3, vin2a_d19.off */ + + /* P8_16 (ball B4) gpio4_29 */ + BONE_PIN(P8_16, default, P8_16( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vin2a_d21.gpio4_29 */ + BONE_PIN(P8_16, gpio, P8_16( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* vin2a_d21.gpio4_29 */ + BONE_PIN(P8_16, gpio_pu, P8_16( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* vin2a_d21.gpio4_29 */ + BONE_PIN(P8_16, gpio_pd, P8_16( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vin2a_d21.gpio4_29 */ + BONE_PIN(P8_16, pruin, P8_16( PIN_INPUT | MUX_MODE12 )) /* vin2a_d21.pr1_pru1_gpi18 */ + BONE_PIN(P8_16, pruout, P8_16( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* vin2a_d21.pr1_pru1_gpo18 */ + + /* P8_16 dummy nodes for unavailable bone bus pins*/ + P8_16_eqep_pin: pinmux_P8_16_eqep_pin { pinctrl-single,pins = < /* To avoid FDT_ERROR on BBAI when using BONE-EQEP2.dtbo */ + /* Default pinmux configuration of P8_16 (P8_16_default_pin) */ + P8_16( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) >; }; /* vin2a_d21.gpio4_29 */ + + /* P8_17 (ball A7) gpio8_18 */ + BONE_PIN(P8_17, default, P8_17( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vout1_d18.gpio8_18 */ + BONE_PIN(P8_17, gpio, P8_17( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* vout1_d18.gpio8_18 */ + BONE_PIN(P8_17, gpio_pu, P8_17( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* vout1_d18.gpio8_18 */ + BONE_PIN(P8_17, gpio_pd, P8_17( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vout1_d18.gpio8_18 */ + BONE_PIN(P8_17, pruin, P8_17( PIN_INPUT | MUX_MODE12 )) /* vout1_d18.pr2_pru0_gpi15 */ + BONE_PIN(P8_17, pruout, P8_17( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* vout1_d18.pr2_pru0_gpo15 */ + + /* P8_18 (ball F5) gpio4_9 */ + BONE_PIN(P8_18, default, P8_18( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vin2a_d8.gpio4_9 */ + BONE_PIN(P8_18, gpio, P8_18( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* vin2a_d8.gpio4_9 */ + BONE_PIN(P8_18, gpio_pu, P8_18( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* vin2a_d8.gpio4_9 */ + BONE_PIN(P8_18, gpio_pd, P8_18( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vin2a_d8.gpio4_9 */ + BONE_PIN(P8_18, eqep, P8_18( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE10 )) /* vin2a_d8.eQEP2_strobe */ + BONE_PIN(P8_18, pruin, P8_18( PIN_INPUT | MUX_MODE12 )) /* vin2a_d8.pr1_pru1_gpi5 */ + BONE_PIN(P8_18, pruout, P8_18( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* vin2a_d8.pr1_pru1_gpo5 */ + + /* P8_19 (ball E6) gpio4_10 */ + BONE_PIN(P8_19, default, P8_19( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vin2a_d9.gpio4_10 */ + BONE_PIN(P8_19, gpio, P8_19( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* vin2a_d9.gpio4_10 */ + BONE_PIN(P8_19, gpio_pu, P8_19( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* vin2a_d9.gpio4_10 */ + BONE_PIN(P8_19, gpio_pd, P8_19( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vin2a_d9.gpio4_10 */ + BONE_PIN(P8_19, pwm, P8_19( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE10 )) /* vin2a_d9.ehrpwm2A */ + BONE_PIN(P8_19, pruin, P8_19( PIN_INPUT | MUX_MODE12 )) /* vin2a_d9.pr1_pru1_gpi6 */ + BONE_PIN(P8_19, pruout, P8_19( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* vin2a_d9.pr1_pru1_gpo6 */ + + /* P8_20 (ball AC4) gpio6_30 */ + BONE_PIN(P8_20, default, P8_20( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* mmc3_cmd.gpio6_30 */ + BONE_PIN(P8_20, gpio, P8_20( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* mmc3_cmd.gpio6_30 */ + BONE_PIN(P8_20, gpio_pu, P8_20( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* mmc3_cmd.gpio6_30 */ + BONE_PIN(P8_20, gpio_pd, P8_20( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* mmc3_cmd.gpio6_30 */ + BONE_PIN(P8_20, pruout, P8_20( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* mmc3_cmd.pr2_pru0_gpo3 */ + BONE_PIN(P8_20, pruin, P8_20( PIN_INPUT | MUX_MODE12 )) /* mmc3_cmd.pr2_pru0_gpi3 */ + BONE_PIN(P8_20, ecap_pwm, P8_20( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE10 )) /* mmc3_cmd.eCAP2_in_PWM2_out */ + + /* P8_21 (ball AD4) gpio6_29 */ + BONE_PIN(P8_21, default, P8_21( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* mmc3_clk.gpio6_29 */ + BONE_PIN(P8_21, gpio, P8_21( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* mmc3_clk.gpio6_29 */ + BONE_PIN(P8_21, gpio_pu, P8_21( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* mmc3_clk.gpio6_29 */ + BONE_PIN(P8_21, gpio_pd, P8_21( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* mmc3_clk.gpio6_29 */ + BONE_PIN(P8_21, pruout, P8_21( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* mmc3_clk.pr2_pru0_gpo2 */ + BONE_PIN(P8_21, pruin, P8_21( PIN_INPUT | MUX_MODE12 )) /* mmc3_clk.pr2_pru0_gpi2 */ + + /* P8_22 (ball AD6) gpio1_23 */ + BONE_PIN(P8_22, default, P8_22( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* mmc3_dat5.gpio1_23 */ + BONE_PIN(P8_22, gpio, P8_22( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* mmc3_dat5.gpio1_23 */ + BONE_PIN(P8_22, gpio_pu, P8_22( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* mmc3_dat5.gpio1_23 */ + BONE_PIN(P8_22, gpio_pd, P8_22( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* mmc3_dat5.gpio1_23 */ + BONE_PIN(P8_22, pruout, P8_22( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* mmc3_dat5.pr2_pru0_gpo9 */ + BONE_PIN(P8_22, pruin, P8_22( PIN_INPUT | MUX_MODE12 )) /* mmc3_dat5.pr2_pru0_gpi9 */ + + /* P8_23 (ball AC8) gpio1_22 */ + BONE_PIN(P8_23, default, P8_23( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* mmc3_dat4.gpio1_22 */ + BONE_PIN(P8_23, gpio, P8_23( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* mmc3_dat4.gpio1_22 */ + BONE_PIN(P8_23, gpio_pu, P8_23( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* mmc3_dat4.gpio1_22 */ + BONE_PIN(P8_23, gpio_pd, P8_23( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* mmc3_dat4.gpio1_22 */ + BONE_PIN(P8_23, pruout, P8_23( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* mmc3_dat4.pr2_pru0_gpo8 */ + BONE_PIN(P8_23, pruin, P8_23( PIN_INPUT | MUX_MODE12 )) /* mmc3_dat4.pr2_pru0_gpi8 */ + + /* P8_24 (ball AC6) gpio7_0 */ + BONE_PIN(P8_24, default, P8_24( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* mmc3_dat1.gpio7_0 */ + BONE_PIN(P8_24, gpio, P8_24( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* mmc3_dat1.gpio7_0 */ + BONE_PIN(P8_24, gpio_pu, P8_24( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* mmc3_dat1.gpio7_0 */ + BONE_PIN(P8_24, gpio_pd, P8_24( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* mmc3_dat1.gpio7_0 */ + BONE_PIN(P8_24, pruout, P8_24( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* mmc3_dat1.pr2_pru0_gpo5 */ + BONE_PIN(P8_24, pruin, P8_24( PIN_INPUT | MUX_MODE12 )) /* mmc3_dat1.pr2_pru0_gpi5 */ + + /* P8_25 (ball AC7) gpio6_31 */ + BONE_PIN(P8_25, default, P8_25( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* mmc3_dat0.gpio6_31 */ + BONE_PIN(P8_25, gpio, P8_25( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* mmc3_dat0.gpio6_31 */ + BONE_PIN(P8_25, gpio_pu, P8_25( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* mmc3_dat0.gpio6_31 */ + BONE_PIN(P8_25, gpio_pd, P8_25( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* mmc3_dat0.gpio6_31 */ + BONE_PIN(P8_25, pruout, P8_25( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* mmc3_dat0.pr2_pru0_gpo4 */ + BONE_PIN(P8_25, pruin, P8_25( PIN_INPUT | MUX_MODE12 )) /* mmc3_dat0.pr2_pru0_gpi4 */ + + /* P8_26 (ball B3) gpio4_28 */ + BONE_PIN(P8_26, default, P8_26( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* vin2a_d20.gpio4_28 */ + BONE_PIN(P8_26, gpio, P8_26( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* vin2a_d20.gpio4_28 */ + BONE_PIN(P8_26, gpio_pu, P8_26( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* vin2a_d20.gpio4_28 */ + BONE_PIN(P8_26, gpio_pd, P8_26( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vin2a_d20.gpio4_28 */ + BONE_PIN(P8_26, pruin, P8_26( PIN_INPUT | MUX_MODE12 )) /* vin2a_d20.pr1_pru1_gpi17 */ + BONE_PIN(P8_26, pruout, P8_26( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* vin2a_d20.pr1_pru1_gpo17 */ + BONE_PIN(P8_26, ecap_pwm, P8_26( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE10 )) /* vin2a_d20.eCAP3_in_PWM3_out */ + + /* P8_27a (ball E11) gpio4_23 */ + BONE_PIN(P8_27, default, P8_27A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_27B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_vsync.gpio4_23, vout1_d19.off */ + BONE_PIN(P8_27, gpio, P8_27A( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P8_27B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_vsync.gpio4_23, vout1_d19.off */ + BONE_PIN(P8_27, gpio_pu, P8_27A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P8_27B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_vsync.gpio4_23, vout1_d19.off */ + BONE_PIN(P8_27, gpio_pd, P8_27A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_27B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_vsync.gpio4_23, vout1_d19.off */ + BONE_PIN(P8_27, lcd, P8_27A( PIN_OUTPUT | MUX_MODE0) P8_27B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_vsync.vout1_vsync, vout1_d19.off */ + BONE_PIN(P8_27, pruout, P8_27A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13) P8_27B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_vsync.pr2_pru1_gpo17, vout1_d19.off */ + BONE_PIN(P8_27, pruin, P8_27A( PIN_INPUT | MUX_MODE12) P8_27B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_vsync.pr2_pru1_gpi17, vout1_d19.off */ + + /* P8_27b (ball A8) gpio8_19 */ + + /* P8_28a (ball D11) gpio4_19 */ + BONE_PIN(P8_28, default, P8_28A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_28B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_clk.gpio4_19, vout1_d20.off */ + BONE_PIN(P8_28, gpio, P8_28A( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P8_28B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_clk.gpio4_19, vout1_d20.off */ + BONE_PIN(P8_28, gpio_pu, P8_28A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P8_28B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_clk.gpio4_19, vout1_d20.off */ + BONE_PIN(P8_28, gpio_pd, P8_28A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_28B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_clk.gpio4_19, vout1_d20.off */ + BONE_PIN(P8_28, lcd, P8_28A( PIN_OUTPUT | MUX_MODE0) P8_28B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_clk.vout1_clk, vout1_d20.off */ + + /* P8_28b (ball C9) gpio8_20 */ + BONE_PIN(P8_28, pruout, P8_28B( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13) P8_28A( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d20.pr2_pru0_gpo17, vout1_clk.off */ + BONE_PIN(P8_28, pruin, P8_28B( PIN_INPUT | MUX_MODE12) P8_28A( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d20.pr2_pru0_gpi17, vout1_clk.off */ + + /* P8_29a (ball C11) gpio4_22 */ + BONE_PIN(P8_29, default, P8_29A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_29B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_hsync.gpio4_22, vout1_d21.off */ + BONE_PIN(P8_29, gpio, P8_29A( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P8_29B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_hsync.gpio4_22, vout1_d21.off */ + BONE_PIN(P8_29, gpio_pu, P8_29A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P8_29B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_hsync.gpio4_22, vout1_d21.off */ + BONE_PIN(P8_29, gpio_pd, P8_29A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_29B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_hsync.gpio4_22, vout1_d21.off */ + BONE_PIN(P8_29, lcd, P8_29A( PIN_OUTPUT | MUX_MODE0) P8_29B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_hsync.vout1_hsync, vout1_d21.off */ + + /* P8_29b (ball A9) gpio8_21 */ + BONE_PIN(P8_29, pruout, P8_29B( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13) P8_29A( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d21.pr2_pru0_gpo18, vout1_hsync.off */ + BONE_PIN(P8_29, pruin, P8_29B( PIN_INPUT | MUX_MODE12) P8_29A( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d21.pr2_pru0_gpi18, vout1_hsync.off */ + + /* P8_30a (ball B10) gpio4_20 */ + BONE_PIN(P8_30, default, P8_30A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_30B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_de.gpio4_20, vout1_d22.off */ + BONE_PIN(P8_30, gpio, P8_30A( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P8_30B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_de.gpio4_20, vout1_d22.off */ + BONE_PIN(P8_30, gpio_pu, P8_30A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P8_30B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_de.gpio4_20, vout1_d22.off */ + BONE_PIN(P8_30, gpio_pd, P8_30A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_30B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_de.gpio4_20, vout1_d22.off */ + BONE_PIN(P8_30, lcd, P8_30A( PIN_OUTPUT | MUX_MODE0) P8_30B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_de.vout1_de, vout1_d22.off */ + + /* P8_30b (ball B9) gpio8_22 */ + BONE_PIN(P8_30, pruout, P8_30B( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13) P8_30A( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d22.pr2_pru0_gpo19, vout1_de.off */ + BONE_PIN(P8_30, pruin, P8_30B( PIN_INPUT | MUX_MODE12) P8_30A( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d22.pr2_pru0_gpi19, vout1_de.off */ + + + /* P8_31a (ball C8) gpio8_14 */ + BONE_PIN(P8_31, default, P8_31A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_31B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d14.gpio8_14, mcasp4_axr0.off */ + BONE_PIN(P8_31, gpio, P8_31A( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P8_31B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d14.gpio8_14, mcasp4_axr0.off */ + BONE_PIN(P8_31, gpio_pu, P8_31A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P8_31B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d14.gpio8_14, mcasp4_axr0.off */ + BONE_PIN(P8_31, gpio_pd, P8_31A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_31B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d14.gpio8_14, mcasp4_axr0.off */ + BONE_PIN(P8_31, lcd, P8_31A( PIN_OUTPUT | MUX_MODE0) P8_31B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d14.vout1_d14, mcasp4_axr0.off */ + BONE_PIN(P8_31, pruout, P8_31A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13) P8_31B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d14.pr2_pru0_gpo11, mcasp4_axr0.off */ + BONE_PIN(P8_31, pruin, P8_31A( PIN_INPUT | MUX_MODE12) P8_31B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d14.pr2_pru0_gpi11, mcasp4_axr0.off */ + BONE_PIN(P8_31, pru_uart, P8_31A( PIN_OUTPUT_PULLUP | MUX_MODE10) P8_31B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d14.pr2_uart0_txd, mcasp4_axr0.off */ + + /* P8_31b (ball G16) */ + BONE_PIN(P8_31, uart, P8_31B( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) P8_31A( PIN_OUTPUT | MUX_MODE15 )) /* mcasp4_axr0.uart4_rxd,vout1_d14.off */ + + /* P8_31 dummy nodes for unavailable bone bus pins*/ + P8_31_eqep_pin: pinmux_P8_31_eqep_pin { pinctrl-single,pins = < /* To avoid FDT_ERROR on BBAI when using BONE-EQEP1.dtbo */ + /* Default pinmux configuration of P8_31 (P8_31_default_pin) */ + P8_31A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_31B( PIN_OUTPUT | MUX_MODE15) >; }; /* vout1_d14.gpio8_14, mcasp4_axr0.off */ + + /* P8_32a (ball C7) gpio8_15 */ + BONE_PIN(P8_32, default, P8_32A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_32B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d15.gpio8_15, mcasp4_axr1.off */ + BONE_PIN(P8_32, gpio, P8_32A( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P8_32B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d15.gpio8_15, mcasp4_axr1.off */ + BONE_PIN(P8_32, gpio_pu, P8_32A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P8_32B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d15.gpio8_15, mcasp4_axr1.off */ + BONE_PIN(P8_32, gpio_pd, P8_32A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_32B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d15.gpio8_15, mcasp4_axr1.off */ + BONE_PIN(P8_32, lcd, P8_32A( PIN_OUTPUT | MUX_MODE0) P8_32B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d15.vout1_d15, mcasp4_axr1.off */ + BONE_PIN(P8_32, pru_ecap_pwm, P8_32A( PIN_OUTPUT | MUX_MODE10) P8_32B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d15.pr2_ecap0_ecap_capin_apwm_o, mcasp4_axr1.off */ + + /* P8_32b (ball D17) */ + BONE_PIN(P8_32, uart, P8_32B( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) P8_32A( PIN_OUTPUT | MUX_MODE15 )) /* mcasp4_axr1.uart4_txd, vout1_d15.off */ + BONE_PIN(P8_32, pruout, P8_32B( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13) P8_32A( PIN_OUTPUT | MUX_MODE15 )) /* mcasp4_axr1.pr2_pru1_gpo0, vout1_d15.off */ + BONE_PIN(P8_32, pruin, P8_32B( PIN_INPUT | MUX_MODE12) P8_32A( PIN_OUTPUT | MUX_MODE15 )) /* mcasp4_axr1.pr2_pru1_gpi0, vout1_d15.off */ + + /* P8_32 dummy nodes for unavailable bone bus pins*/ + P8_32_eqep_pin: pinmux_P8_32_eqep_pin { pinctrl-single,pins = < /* To avoid FDT_ERROR on BBAI when using BONE-EQEP1.dtbo */ + /* Default pinmux configuration of P8_32 (P8_32_default_pin) */ + P8_32A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_32B( PIN_OUTPUT | MUX_MODE15) >; }; /* vout1_d15.gpio8_15, mcasp4_axr1.off */ + + /* P8_33a (ball C6) gpio8_13 */ + BONE_PIN(P8_33, default, P8_33A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_33B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d13.gpio8_13, vin1a_fld0.off */ + BONE_PIN(P8_33, gpio, P8_33A( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P8_33B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d13.gpio8_13, vin1a_fld0.off */ + BONE_PIN(P8_33, gpio_pu, P8_33A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P8_33B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d13.gpio8_13, vin1a_fld0.off */ + BONE_PIN(P8_33, gpio_pd, P8_33A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_33B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d13.gpio8_13, vin1a_fld0.off */ + BONE_PIN(P8_33, lcd, P8_33A( PIN_OUTPUT | MUX_MODE0) P8_33B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d13.vout1_d13, vin1a_fld0.off */ + BONE_PIN(P8_33, pruout, P8_33A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13) P8_33B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d13.pr2_pru0_gpo10, vin1a_fld0.off */ + BONE_PIN(P8_33, pruin, P8_33A( PIN_INPUT | MUX_MODE12) P8_33B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d13.pr2_pru0_gpi10, vin1a_fld0.off */ + BONE_PIN(P8_33, pru_uart, P8_33A( PIN_INPUT_PULLUP | MUX_MODE10) P8_33B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d13.pr2_uart0_rxd, vin1a_fld0.off */ + + /* P8_33b (ball AF9) gpio3_1 */ + BONE_PIN(P8_33, eqep, P8_33B( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE10) P8_33A( PIN_OUTPUT | MUX_MODE15 )) /* vin1a_fld0.eQEP1B_in, vout1_d13.off */ + + + /* P8_34a (ball D8) gpio8_11 */ + BONE_PIN(P8_34, default, P8_34A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_34B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d11.gpio8_11, vin2a_vsync0.off */ + BONE_PIN(P8_34, gpio, P8_34A( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P8_34B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d11.gpio8_11, vin2a_vsync0.off */ + BONE_PIN(P8_34, gpio_pu, P8_34A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P8_34B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d11.gpio8_11, vin2a_vsync0.off */ + BONE_PIN(P8_34, gpio_pd, P8_34A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_34B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d11.gpio8_11, vin2a_vsync0.off */ + BONE_PIN(P8_34, lcd, P8_34A( PIN_OUTPUT | MUX_MODE0) P8_34B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d11.vout1_d11, vin2a_vsync0.off */ + BONE_PIN(P8_34, pruout, P8_34A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13) P8_34B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d11.pr2_pru0_gpo8, vin2a_vsync0.off */ + BONE_PIN(P8_34, pruin, P8_34A( PIN_INPUT | MUX_MODE12) P8_34B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d11.pr2_pru0_gpi8, vin2a_vsync0.off */ + BONE_PIN(P8_34, pru_uart, P8_34A( PIN_INPUT_PULLUP | MUX_MODE10) P8_34B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d11.pr2_uart0_cts_n, vin2a_vsync0.off */ + + /* P8_34b (ball G6) gpio4_0 */ + BONE_PIN(P8_34, pwm, P8_34B( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE10) P8_34A( PIN_OUTPUT | MUX_MODE15 )) /* vin2a_vsync0.ehrpwm1A, vout1_d11.off */ + + /* P8_35a (ball A5) gpio8_12 */ + BONE_PIN(P8_35, default, P8_35A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_35B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d12.gpio8_12, vin1a_de0.off */ + BONE_PIN(P8_35, gpio, P8_35A( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P8_35B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d12.gpio8_12, vin1a_de0.off */ + BONE_PIN(P8_35, gpio_pu, P8_35A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P8_35B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d12.gpio8_12, vin1a_de0.off */ + BONE_PIN(P8_35, gpio_pd, P8_35A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_35B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d12.gpio8_12, vin1a_de0.off */ + BONE_PIN(P8_35, lcd, P8_35A( PIN_OUTPUT | MUX_MODE0) P8_35B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d12.vout1_d12, vin1a_de0.off */ + BONE_PIN(P8_35, pruout, P8_35A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13) P8_35B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d12.pr2_pru0_gpo9, vin1a_de0.off */ + BONE_PIN(P8_35, pruin, P8_35A( PIN_INPUT | MUX_MODE12) P8_35B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d12.pr2_pru0_gpi9, vin1a_de0.off */ + BONE_PIN(P8_35, pru_uart, P8_35A( PIN_OUTPUT_PULLUP | MUX_MODE10) P8_35B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d12.pr2_uart0_rts_n, vin1a_de0.off */ + + /* P8_35b (ball AD9) gpio3_0 */ + BONE_PIN(P8_35, eqep, P8_35B( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE10) P8_35A( PIN_OUTPUT | MUX_MODE15 )) /* vin1a_de0.eQEP1A_in, vout1_d12.off */ + + /* P8_36a (ball D7) gpio8_10 */ + BONE_PIN(P8_36, default, P8_36A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_36B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d10.gpio8_10, vin2a_d0.off */ + BONE_PIN(P8_36, gpio, P8_36A( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P8_36B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d10.gpio8_10, vin2a_d0.off */ + BONE_PIN(P8_36, gpio_pu, P8_36A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P8_36B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d10.gpio8_10, vin2a_d0.off */ + BONE_PIN(P8_36, gpio_pd, P8_36A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_36B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d10.gpio8_10, vin2a_d0.off */ + BONE_PIN(P8_36, lcd, P8_36A( PIN_OUTPUT | MUX_MODE0) P8_36B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d10.vout1_d10, vin2a_d0.off */ + BONE_PIN(P8_36, pruout, P8_36A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13) P8_36B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d10.pr2_pru0_gpo7, vin2a_d0.off */ + BONE_PIN(P8_36, pruin, P8_36A( PIN_INPUT | MUX_MODE12) P8_36B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d10.pr2_pru0_gpi7, vin2a_d0.off */ + + /* P8_36b (ball F2) gpio4_1 */ + BONE_PIN(P8_36, pwm, P8_36B( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE10) P8_36A( PIN_OUTPUT | MUX_MODE15 )) /* vin2a_d0.ehrpwm1B, vout1_d10.off */ + + /* P8_37a (ball E8) gpio8_8 */ + BONE_PIN(P8_37, default, P8_37A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_37B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d8.gpio8_8, mcasp4_fsx.off */ + BONE_PIN(P8_37, gpio, P8_37A( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P8_37B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d8.gpio8_8, mcasp4_fsx.off */ + BONE_PIN(P8_37, gpio_pu, P8_37A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P8_37B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d8.gpio8_8, mcasp4_fsx.off */ + BONE_PIN(P8_37, gpio_pd, P8_37A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_37B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d8.gpio8_8, mcasp4_fsx.off */ + BONE_PIN(P8_37, lcd, P8_37A( PIN_OUTPUT | MUX_MODE0) P8_37B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d8.vout1_d8, mcasp4_fsx.off */ + BONE_PIN(P8_37, pruout, P8_37A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13) P8_37B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d8.pr2_pru0_gpo5, mcasp4_fsx.off */ + BONE_PIN(P8_37, pruin, P8_37A( PIN_INPUT | MUX_MODE12) P8_37B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d8.pr2_pru0_gpi5, mcasp4_fsx.off */ + + /* P8_37b (ball A21) */ + BONE_PIN(P8_37, uart, P8_37B( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) P8_37A( PIN_OUTPUT | MUX_MODE15 )) /* mcasp4_fsx.uart8_txd. vout1_d8.off */ + + /* P8_38a (ball D9) gpio8_9 */ + BONE_PIN(P8_38, default, P8_38A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_38B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d9.gpio8_9, mcasp4_aclkx.off */ + BONE_PIN(P8_38, gpio, P8_38A( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P8_38B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d9.gpio8_9, mcasp4_aclkx.off */ + BONE_PIN(P8_38, gpio_pu, P8_38A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P8_38B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d9.gpio8_9, mcasp4_aclkx.off */ + BONE_PIN(P8_38, gpio_pd, P8_38A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_38B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d9.gpio8_9, mcasp4_aclkx.off */ + BONE_PIN(P8_38, lcd, P8_38A( PIN_OUTPUT | MUX_MODE0) P8_38B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d9.vout1_d9, mcasp4_aclkx.off */ + BONE_PIN(P8_38, pruout, P8_38A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13) P8_38B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d9.pr2_pru0_gpo6, mcasp4_aclkx.off */ + BONE_PIN(P8_38, pruin, P8_38A( PIN_INPUT | MUX_MODE12) P8_38B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d9.pr2_pru0_gpi6, mcasp4_aclkx.off */ + + /* P8_38b (ball C18) */ + BONE_PIN(P8_38, uart, P8_38B( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) P8_38A( PIN_OUTPUT | MUX_MODE15 )) /* mcasp4_aclkx.uart8_rxd, vout1_d9.off */ + + /* P8_39 (ball F8) gpio8_6 */ + BONE_PIN(P8_39, default, P8_39( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vout1_d6.gpio8_6 */ + BONE_PIN(P8_39, gpio, P8_39( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* vout1_d6.gpio8_6 */ + BONE_PIN(P8_39, gpio_pu, P8_39( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* vout1_d6.gpio8_6 */ + BONE_PIN(P8_39, gpio_pd, P8_39( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vout1_d6.gpio8_6 */ + BONE_PIN(P8_39, pruout, P8_39( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* vout1_d6.pr2_pru0_gpo3 */ + BONE_PIN(P8_39, pruin, P8_39( PIN_INPUT | MUX_MODE12 )) /* vout1_d6.pr2_pru0_gpi3 */ + BONE_PIN(P8_39, lcd, P8_39( PIN_OUTPUT | MUX_MODE0 )) /* vout1_d6.vout1_d6 */ + + /* P8_39 dummy nodes for unavailable bone bus pins*/ + P8_39_eqep_pin: pinmux_P8_39_eqep_pin { pinctrl-single,pins = < /* To avoid FDT_ERROR on BBAI when using BONE-EQEP2A.dtbo */ + /* Default pinmux configuration of P8_39 (P8_39_default_pin) */ + P8_39( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) >; }; /* vout1_d6.gpio8_6 */ + + /* P8_40 (ball E7) gpio8_7 */ + BONE_PIN(P8_40, default, P8_40( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vout1_d7.gpio8_7 */ + BONE_PIN(P8_40, gpio, P8_40( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* vout1_d7.gpio8_7 */ + BONE_PIN(P8_40, gpio_pu, P8_40( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* vout1_d7.gpio8_7 */ + BONE_PIN(P8_40, gpio_pd, P8_40( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vout1_d7.gpio8_7 */ + BONE_PIN(P8_40, pruout, P8_40( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* vout1_d7.pr2_pru0_gpo4 */ + BONE_PIN(P8_40, pruin, P8_40( PIN_INPUT | MUX_MODE12 )) /* vout1_d7.pr2_pru0_gpi4 */ + BONE_PIN(P8_40, lcd, P8_40( PIN_OUTPUT | MUX_MODE0 )) /* vout1_d7.vout1_d7 */ + + /* P8_40 dummy nodes for unavailable bone bus pins*/ + P8_40_eqep_pin: pinmux_P8_40_eqep_pin { pinctrl-single,pins = < /* To avoid FDT_ERROR on BBAI when using BONE-EQEP2A.dtbo */ + /* Default pinmux configuration of P8_40 (P8_40_default_pin) */ + P8_40( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) >; }; /* vout1_d7.gpio8_7 */ + + /* P8_41 (ball E9) gpio8_4 */ + BONE_PIN(P8_41, default, P8_41( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vout1_d4.gpio8_4 */ + BONE_PIN(P8_41, gpio, P8_41( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* vout1_d4.gpio8_4 */ + BONE_PIN(P8_41, gpio_pu, P8_41( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* vout1_d4.gpio8_4 */ + BONE_PIN(P8_41, gpio_pd, P8_41( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vout1_d4.gpio8_4 */ + BONE_PIN(P8_41, pruout, P8_41( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* vout1_d4.pr2_pru0_gpo1 */ + BONE_PIN(P8_41, pruin, P8_41( PIN_INPUT | MUX_MODE12 )) /* vout1_d4.pr2_pru0_gpi1 */ + BONE_PIN(P8_41, lcd, P8_41( PIN_OUTPUT | MUX_MODE0 )) /* vout1_d4.vout1_d4 */ + + /* P8_41 dummy nodes for unavailable bone bus pins*/ + P8_41_eqep_pin: pinmux_P8_41_eqep_pin { pinctrl-single,pins = < /* To avoid FDT_ERROR on BBAI when using BONE-EQEP2A.dtbo */ + /* Default pinmux configuration of P8_41 (P8_41_default_pin) */ + P8_41( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) >; }; /* vout1_d4.gpio8_4 */ + + /* P8_42 (ball F9) gpio8_5 */ + BONE_PIN(P8_42, default, P8_42( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vout1_d5.gpio8_5 */ + BONE_PIN(P8_42, gpio, P8_42( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* vout1_d5.gpio8_5 */ + BONE_PIN(P8_42, gpio_pu, P8_42( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* vout1_d5.gpio8_5 */ + BONE_PIN(P8_42, gpio_pd, P8_42( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vout1_d5.gpio8_5 */ + BONE_PIN(P8_42, pruout, P8_42( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* vout1_d5.pr2_pru0_gpo2 */ + BONE_PIN(P8_42, pruin, P8_42( PIN_INPUT | MUX_MODE12 )) /* vout1_d5.pr2_pru0_gpi2 */ + BONE_PIN(P8_42, lcd, P8_42( PIN_OUTPUT | MUX_MODE0 )) /* vout1_d5.vout1_d5 */ + + /* P8_42 dummy nodes for unavailable bone bus pins*/ + P8_42_eqep_pin: pinmux_P8_42_eqep_pin { pinctrl-single,pins = < /* To avoid FDT_ERROR on BBAI when using BONE-EQEP2A.dtbo */ + /* Default pinmux configuration of P8_42 (P8_42_default_pin) */ + P8_42( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) >; }; /* vout1_d5.gpio8_5 */ + + /* P8_43 (ball F10) gpio8_2 */ + BONE_PIN(P8_43, default, P8_43( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vout1_d2.gpio8_2 */ + BONE_PIN(P8_43, gpio, P8_43( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* vout1_d2.gpio8_2 */ + BONE_PIN(P8_43, gpio_pu, P8_43( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* vout1_d2.gpio8_2 */ + BONE_PIN(P8_43, gpio_pd, P8_43( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vout1_d2.gpio8_2 */ + BONE_PIN(P8_43, pruout, P8_43( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* vout1_d2.pr2_pru1_gpo20 */ + BONE_PIN(P8_43, pruin, P8_43( PIN_INPUT | MUX_MODE12 )) /* vout1_d2.pr2_pru1_gpi20 */ + BONE_PIN(P8_43, lcd, P8_43( PIN_OUTPUT | MUX_MODE0 )) /* vout1_d2.vout1_d2 */ + BONE_PIN(P8_43, pru_uart, P8_43( PIN_INPUT_PULLUP | MUX_MODE10 )) /* vout1_d2.pr1_uart0_rxd */ + + /* P8_44 (ball G11) gpio8_3 */ + BONE_PIN(P8_44, default, P8_44( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vout1_d3.gpio8_3 */ + BONE_PIN(P8_44, gpio, P8_44( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* vout1_d3.gpio8_3 */ + BONE_PIN(P8_44, gpio_pu, P8_44( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* vout1_d3.gpio8_3 */ + BONE_PIN(P8_44, gpio_pd, P8_44( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vout1_d3.gpio8_3 */ + BONE_PIN(P8_44, pruout, P8_44( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* vout1_d3.pr2_pru0_gpo0 */ + BONE_PIN(P8_44, pruin, P8_44( PIN_INPUT | MUX_MODE12 )) /* vout1_d3.pr2_pru0_gpi0 */ + BONE_PIN(P8_44, lcd, P8_44( PIN_OUTPUT | MUX_MODE0 )) /* vout1_d3.vout1_d3 */ + BONE_PIN(P8_44, pru_uart, P8_44( PIN_OUTPUT_PULLUP | MUX_MODE10 )) /* vout1_d3.pr1_uart0_txd */ + + /* P8_45a (ball F11) gpio8_0 */ + BONE_PIN(P8_45, default, P8_45A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_45B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d0.gpio8_0, vout1_d16.off */ + BONE_PIN(P8_45, gpio, P8_45A( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P8_45B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d0.gpio8_0, vout1_d16.off */ + BONE_PIN(P8_45, gpio_pu, P8_45A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P8_45B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d0.gpio8_0, vout1_d16.off */ + BONE_PIN(P8_45, gpio_pd, P8_45A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_45B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d0.gpio8_0, vout1_d16.off */ + BONE_PIN(P8_45, lcd, P8_45A( PIN_OUTPUT | MUX_MODE0) P8_45B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d0.vout1_d0, vout1_d16.off */ + BONE_PIN(P8_45, pruout, P8_45A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13) P8_45B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d0.pr2_pru1_gpo18, vout1_d16.off */ + BONE_PIN(P8_45, pruin, P8_45A( PIN_INPUT | MUX_MODE12) P8_45B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d0.pr2_pru1_gpi18, vout1_d16.off */ + BONE_PIN(P8_45, pru_uart, P8_45A( PIN_INPUT_PULLUP | MUX_MODE10) P8_45B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d0.pr1_uart0_cts_n, vout1_d16.off */ + + /* P8_45b (ball B7) gpio8_16 */ + + /* P8_46a (ball G10) gpio8_1 */ + BONE_PIN(P8_46, default, P8_46A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_46B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d1.gpio8_1, vout1_d23.off */ + BONE_PIN(P8_46, gpio, P8_46A( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P8_46B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d1.gpio8_1, vout1_d23.off */ + BONE_PIN(P8_46, gpio_pu, P8_46A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P8_46B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d1.gpio8_1, vout1_d23.off */ + BONE_PIN(P8_46, gpio_pd, P8_46A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P8_46B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d1.gpio8_1, vout1_d23.off */ + BONE_PIN(P8_46, lcd, P8_46A( PIN_OUTPUT | MUX_MODE0) P8_46B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d1.vout1_d1, vout1_d23.off */ + BONE_PIN(P8_46, pruout, P8_46A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13) P8_46B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d1.pr2_pru1_gpo19, vout1_d23.off */ + BONE_PIN(P8_46, pruin, P8_46A( PIN_INPUT | MUX_MODE12) P8_46B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d1.pr2_pru1_gpi19, vout1_d23.off */ + BONE_PIN(P8_46, pru_uart, P8_46A( PIN_OUTPUT_PULLUP | MUX_MODE10) P8_46B( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d1.pr1_uart0_rts_n, vout1_d23.off */ + + /* P8_46b (ball A10) gpio8_23 */ + + /************************/ + /* P9 Header */ + /************************/ + + /* P9_01 GND */ + + /* P9_02 GND */ + + /* P9_03 3V3 */ + + /* P9_04 3V3 */ + + /* P9_05 VDD_5V */ + + /* P9_06 VDD_5V */ + + /* P9_07 SYS_5V */ + + /* P9_08 SYS_5V */ + + /* P9_09 PWR_BUT */ + + /* P9_10 RSTn */ + + /* P9_11a (ball B19) */ + BONE_PIN(P9_11, uart, P9_11A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) P9_11B( PIN_OUTPUT | MUX_MODE15 )) /* mcasp3_axr0.uart5_rxd, vout1_d17.off */ + BONE_PIN(P9_11, pruout, P9_11A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13) P9_11B( PIN_OUTPUT | MUX_MODE15 )) /* mcasp3_axr0.pr2_pru0_gpo14, vout1_d17.off */ + BONE_PIN(P9_11, pruin, P9_11A( PIN_INPUT | MUX_MODE12) P9_11B( PIN_OUTPUT | MUX_MODE15 )) /* mcasp3_axr0.pr2_pru0_gpi14, vout1_d17.off */ + + /* P9_11b (ball B8) gpio8_17 */ + BONE_PIN(P9_11, default, P9_11B( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P9_11A( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d17.gpio8_17, mcasp3_axr0.off */ + BONE_PIN(P9_11, gpio, P9_11B( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P9_11A( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d17.gpio8_17, mcasp3_axr0.off */ + BONE_PIN(P9_11, gpio_pu, P9_11B( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P9_11A( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d17.gpio8_17, mcasp3_axr0.off */ + BONE_PIN(P9_11, gpio_pd, P9_11B( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P9_11A( PIN_OUTPUT | MUX_MODE15 )) /* vout1_d17.gpio8_17, mcasp3_axr0.off */ + + /* P9_12 (ball B14) gpio5_0 */ + BONE_PIN(P9_12, default, P9_12( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* mcasp1_aclkr.gpio5_0 */ + BONE_PIN(P9_12, gpio, P9_12( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* mcasp1_aclkr.gpio5_0 */ + BONE_PIN(P9_12, gpio_pu, P9_12( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* mcasp1_aclkr.gpio5_0 */ + BONE_PIN(P9_12, gpio_pd, P9_12( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* mcasp1_aclkr.gpio5_0 */ + BONE_PIN(P9_12, mcasp, P9_12( PIN_INPUT_PULLDOWN | MUX_MODE0 )) /* mcasp1_aclkr.mcasp1_aclkr */ + + /* P9_13a (ball C17) */ + BONE_PIN(P9_13, uart, P9_13A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE4) P9_13B( PIN_OUTPUT | MUX_MODE15 )) /* mcasp3_axr1.uart5_txd, usb1_drvvbus.off */ + BONE_PIN(P9_13, pruout, P9_13A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13) P9_13B( PIN_OUTPUT | MUX_MODE15 )) /* mcasp3_axr1.pr2_pru0_gpo15, usb1_drvvbus.off */ + BONE_PIN(P9_13, pruin, P9_13A( PIN_INPUT | MUX_MODE12) P9_13B( PIN_OUTPUT | MUX_MODE15 )) /* mcasp3_axr1.pr2_pru0_gpi15, usb1_drvvbus.off */ + + /* P9_13b (ball AB10) gpio6_12 */ + BONE_PIN(P9_13, default, P9_13B( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P9_13A( PIN_OUTPUT | MUX_MODE15 )) /* usb1_drvvbus.gpio6_12, mcasp3_axr1.off */ + BONE_PIN(P9_13, gpio, P9_13B( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P9_13A( PIN_OUTPUT | MUX_MODE15 )) /* usb1_drvvbus.gpio6_12, mcasp3_axr1.off */ + BONE_PIN(P9_13, gpio_pu, P9_13B( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P9_13A( PIN_OUTPUT | MUX_MODE15 )) /* usb1_drvvbus.gpio6_12, mcasp3_axr1.off */ + BONE_PIN(P9_13, gpio_pd, P9_13B( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P9_13A( PIN_OUTPUT | MUX_MODE15 )) /* usb1_drvvbus.gpio6_12, mcasp3_axr1.off */ + + + /* P9_14 (ball D6) gpio4_25 */ + BONE_PIN(P9_14, default, P9_14( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vin2a_d17.gpio4_25 */ + BONE_PIN(P9_14, gpio, P9_14( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* vin2a_d17.gpio4_25 */ + BONE_PIN(P9_14, gpio_pu, P9_14( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* vin2a_d17.gpio4_25 */ + BONE_PIN(P9_14, gpio_pd, P9_14( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vin2a_d17.gpio4_25 */ + BONE_PIN(P9_14, pwm, P9_14( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE10 )) /* vin2a_d17.ehrpwm3A */ + BONE_PIN(P9_14, pruin, P9_14( PIN_INPUT | MUX_MODE12 )) /* vin2a_d17.pr1_pru1_gpi14 */ + BONE_PIN(P9_14, pruout, P9_14( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* vin2a_d17.pr1_pru1_gpo14 */ + + /* P9_15 (ball AG4) gpio3_12 */ + BONE_PIN(P9_15, default, P9_15( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vin1a_d8.gpio3_12 */ + BONE_PIN(P9_15, gpio, P9_15( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* vin1a_d8.gpio3_12 */ + BONE_PIN(P9_15, gpio_pu, P9_15( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* vin1a_d8.gpio3_12 */ + BONE_PIN(P9_15, gpio_pd, P9_15( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vin1a_d8.gpio3_12 */ + BONE_PIN(P9_15, pruin, P9_15( PIN_INPUT | MUX_MODE12 )) /* vin1a_d8.pr1_pru0_gpi5 */ + BONE_PIN(P9_15, pruout, P9_15( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* vin1a_d8.pr1_pru0_gpo5 */ + BONE_PIN(P9_15, eqep, P9_15( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE10 )) /* vin1a_d8.eQEP2_index */ + + /* P9_16 (ball C5) gpio4_26 */ + BONE_PIN(P9_16, default, P9_16( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE10 )) /* vin2a_d18.gpio4_26 */ + BONE_PIN(P9_16, gpio, P9_16( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* vin2a_d18.gpio4_26 */ + BONE_PIN(P9_16, gpio_pu, P9_16( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* vin2a_d18.gpio4_26 */ + BONE_PIN(P9_16, gpio_pd, P9_16( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* vin2a_d18.gpio4_26 */ + BONE_PIN(P9_16, pwm, P9_16( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE10 )) /* vin2a_d18.ehrpwm3B */ + BONE_PIN(P9_16, pruin, P9_16( PIN_INPUT | MUX_MODE12 )) /* vin2a_d18.pr1_pru1_gpi15 */ + BONE_PIN(P9_16, pruout, P9_16( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* vin2a_d18.pr1_pru1_gpo15 */ + + /* P9_17a (ball B24) gpio7_17 */ + BONE_PIN(P9_17, default, P9_17A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P9_17B( PIN_OUTPUT | MUX_MODE15 )) /* spi2_cs0.gpio7_17, mcasp1_axr1.off */ + BONE_PIN(P9_17, gpio, P9_17A( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P9_17B( PIN_OUTPUT | MUX_MODE15 )) /* spi2_cs0.gpio7_17, mcasp1_axr1.off */ + BONE_PIN(P9_17, gpio_pu, P9_17A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P9_17B( PIN_OUTPUT | MUX_MODE15 )) /* spi2_cs0.gpio7_17, mcasp1_axr1.off */ + BONE_PIN(P9_17, gpio_pd, P9_17A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P9_17B( PIN_OUTPUT | MUX_MODE15 )) /* spi2_cs0.gpio7_17, mcasp1_axr1.off */ + BONE_PIN(P9_17, spi_cs, P9_17A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) P9_17B( PIN_OUTPUT | MUX_MODE15 )) /* spi2_cs0.spi2_cs0, mcasp1_axr1.off */ + + /* P9_17b (ball F12) gpio5_3 */ + BONE_PIN(P9_17, i2c, P9_17B( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE10) P9_17A( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_axr1.i2c5_scl, spi2_cs0.off */ + BONE_PIN(P9_17, pru_uart, P9_17B( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) P9_17A( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_axr1.uart6_txd, spi2_cs0.off */ + BONE_PIN(P9_17, pruout, P9_17B( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13) P9_17A( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_axr1.pr2_pru1_gpo9, spi2_cs0.off */ + BONE_PIN(P9_17, pruin, P9_17B( PIN_INPUT | MUX_MODE12) P9_17A( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_axr1.pr2_pru1_gpi9, spi2_cs0.off */ + + /* P9_18a (ball G17) gpio7_16 */ + BONE_PIN(P9_18, default, P9_18A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P9_18B( PIN_OUTPUT | MUX_MODE15 )) /* spi2_d0.gpio7_16, mcasp1_axr0.off */ + BONE_PIN(P9_18, gpio, P9_18A( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P9_18B( PIN_OUTPUT | MUX_MODE15 )) /* spi2_d0.gpio7_16, mcasp1_axr0.off */ + BONE_PIN(P9_18, gpio_pu, P9_18A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P9_18B( PIN_OUTPUT | MUX_MODE15 )) /* spi2_d0.gpio7_16, mcasp1_axr0.off */ + BONE_PIN(P9_18, gpio_pd, P9_18A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P9_18B( PIN_OUTPUT | MUX_MODE15 )) /* spi2_d0.gpio7_16, mcasp1_axr0.off */ + BONE_PIN(P9_18, spi, P9_18A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) P9_18B( PIN_OUTPUT | MUX_MODE15 )) /* spi2_d0.spi2_d0, mcasp1_axr0.off */ + + /* P9_18b (ball G12) gpio5_2 */ + BONE_PIN(P9_18, i2c, P9_18B( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE10) P9_18A( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_axr0.i2c5_sda, spi2_d0.off */ + BONE_PIN(P9_18, pru_uart, P9_18B( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) P9_18A( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_axr0.uart6_rxd, spi2_d0.off */ + BONE_PIN(P9_18, pruout, P9_18B( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13) P9_18A( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_axr0.pr2_pru1_gpo8, spi2_d0.off */ + BONE_PIN(P9_18, pruin, P9_18B( PIN_INPUT | MUX_MODE12) P9_18A( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_axr0.pr2_pru1_gpi8, spi2_d0.off */ + + /* P9_19a (ball R6) gpio7_3 */ + BONE_PIN(P9_19, default, P9_19A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) P9_19B( PIN_OUTPUT | MUX_MODE15 )) /* gpmc_a0.i2c4_scl, gpmc_a0.off */ + BONE_PIN(P9_19, gpio, P9_19A( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P9_19B( PIN_OUTPUT | MUX_MODE15 )) /* gpmc_a0.gpio7_3, gpmc_a0.off */ + BONE_PIN(P9_19, gpio_pu, P9_19A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P9_19B( PIN_OUTPUT | MUX_MODE15 )) /* gpmc_a0.gpio7_3, gpmc_a0.off */ + BONE_PIN(P9_19, gpio_pd, P9_19A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P9_19B( PIN_OUTPUT | MUX_MODE15 )) /* gpmc_a0.gpio7_3, gpmc_a0.off */ + BONE_PIN(P9_19, i2c, P9_19A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) P9_19B( PIN_OUTPUT | MUX_MODE15 )) /* gpmc_a0.i2c4_scl, gpmc_a0.off */ + + /* P9_19b (ball F4) gpio4_6 */ + BONE_PIN(P9_19, eqep, P9_19B( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE10) P9_19A( PIN_OUTPUT | MUX_MODE15 )) /* vin2a_d5.eQEP2A_in, gpmc_a0.off */ + BONE_PIN(P9_19, pruin, P9_19B( PIN_INPUT | MUX_MODE12) P9_19A( PIN_OUTPUT | MUX_MODE15 )) /* vin2a_d5.pr1_pru1_gpi2, gpmc_a0.off */ + BONE_PIN(P9_19, pruout, P9_19B( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13) P9_19A( PIN_OUTPUT | MUX_MODE15 )) /* vin2a_d5.pr1_pru1_gpo2, gpmc_a0.off */ + + /* P9_19 dummy nodes for unavailable bone bus */ + P9_19_can_pin: pinmux_P9_19_default_pin { pinctrl-single,pins = < /* To avoid FDT_ERROR on BBAI when using BONE-CAN0-00A0.dtbo */ + /* Default pinmux configuration of P9_19 (P9_19_default_pin) */ + P9_19A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) P9_19B( PIN_OUTPUT | MUX_MODE15) >; }; /* gpmc_a0.i2c4_scl, gpmc_a0.off */ + + /* P9_20a (ball T9) gpio7_4 */ + BONE_PIN(P9_20, default, P9_20A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) P9_20B( PIN_OUTPUT | MUX_MODE15 )) /* gpmc_a1.i2c4_sda, vin2a_d4.off */ + BONE_PIN(P9_20, gpio, P9_20A( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P9_20B( PIN_OUTPUT | MUX_MODE15 )) /* gpmc_a1.gpio7_4, vin2a_d4.off */ + BONE_PIN(P9_20, gpio_pu, P9_20A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P9_20B( PIN_OUTPUT | MUX_MODE15 )) /* gpmc_a1.gpio7_4, vin2a_d4.off */ + BONE_PIN(P9_20, gpio_pd, P9_20A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P9_20B( PIN_OUTPUT | MUX_MODE15 )) /* gpmc_a1.gpio7_4, vin2a_d4.off */ + BONE_PIN(P9_20, i2c, P9_20A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) P9_20B( PIN_OUTPUT | MUX_MODE15 )) /* gpmc_a1.i2c4_sda, vin2a_d4.off */ + + /* P9_20b (ball D2) gpio4_5*/ + BONE_PIN(P9_20, pruout, P9_20B( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13) P9_20A( PIN_OUTPUT | MUX_MODE15 )) /* vin2a_d4.pr1_pru1_gpo1, gpmc_a1.off */ + BONE_PIN(P9_20, pruin, P9_20B( PIN_INPUT | MUX_MODE12) P9_20A( PIN_OUTPUT | MUX_MODE15 )) /* vin2a_d4.pr1_pru1_gpi1, gpmc_a1.off */ + + /* P9_20 dummy nodes for unavailable bone bus */ + P9_20_can_pin: pinmux_P9_20_default_pin { pinctrl-single,pins = < /* To avoid FDT_ERROR on BBAI when using BONE-CAN0-00A0.dtbo */ + /* Default pinmux configuration of P9_20 (P9_20_default_pin) */ + P9_20A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) P9_20B( PIN_OUTPUT | MUX_MODE15) >; }; /* gpmc_a1.i2c4_sda, vin2a_d4.off */ + + /* P9_21a (ball AF8) gpio3_3 */ + BONE_PIN(P9_21, default, P9_21A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P9_21B( PIN_OUTPUT | MUX_MODE15 )) /* vin1a_vsync0.gpio3_3,spi2_d1.off */ + BONE_PIN(P9_21, gpio, P9_21A( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P9_21B( PIN_OUTPUT | MUX_MODE15 )) /* vin1a_vsync0.gpio3_3,spi2_d1.off */ + BONE_PIN(P9_21, gpio_pu, P9_21A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P9_21B( PIN_OUTPUT | MUX_MODE15 )) /* vin1a_vsync0.gpio3_3,spi2_d1.off */ + BONE_PIN(P9_21, gpio_pd, P9_21A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P9_21B( PIN_OUTPUT | MUX_MODE15 )) /* vin1a_vsync0.gpio3_3,spi2_d1.off */ + BONE_PIN(P9_21, eqep, P9_21A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE10) P9_21B( PIN_OUTPUT | MUX_MODE15 )) /* vin1a_vsync0.eQEP1_strobe,spi2_d1.off */ + BONE_PIN(P9_21, timer, P9_21A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE7) P9_21B( PIN_OUTPUT | MUX_MODE15 )) /* vin1a_vsync0.timer13,spi2_d1.off */ + + /* P9_21b (ball B22) gpio7_15 */ + BONE_PIN(P9_21, spi, P9_21B( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) P9_21A( PIN_OUTPUT | MUX_MODE15 )) /* spi2_d1.spi2_d1, vin1a_vsync0.off */ + BONE_PIN(P9_21, uart, P9_21B( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) P9_21A( PIN_OUTPUT | MUX_MODE15 )) /* spi2_d1.uart3_txd, vin1a_vsync0.off */ + + /* P9_21 dummy nodes for unavailable bone bus */ + P9_21_i2c_pin: pinmux_P9_21_default_pin { pinctrl-single,pins = < /* To avoid FDT_ERROR on BBAI when using BONE-I2C2A-00A0.dtbo */ + /* Default pinmux configuration of P9_21 (P9_21_default_pin) */ + P9_21A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P9_21B( PIN_OUTPUT | MUX_MODE15) >; }; /* vin1a_vsync0.gpio3_3,spi2_d1.off */ + P9_21_pwm_pin: pinmux_P9_21_default_pin { pinctrl-single,pins = < /* To avoid FDT_ERROR on BBAI when using BONE-PWM0-00A0.dtbo */ + /* Default pinmux configuration of P9_21 (P9_21_default_pin) */ + P9_21A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P9_21B( PIN_OUTPUT | MUX_MODE15) >; }; /* vin1a_vsync0.gpio3_3,spi2_d1.off */ + + /* P9_22a (ball B26) gpio6_19 */ + BONE_PIN(P9_22, default, P9_22A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P9_22B( PIN_OUTPUT | MUX_MODE15 )) /* xref_clk2.gpio6_19, spi2_sclk.off */ + BONE_PIN(P9_22, gpio, P9_22A( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P9_22B( PIN_OUTPUT | MUX_MODE15 )) /* xref_clk2.gpio6_19, spi2_sclk.off */ + BONE_PIN(P9_22, gpio_pu, P9_22A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P9_22B( PIN_OUTPUT | MUX_MODE15 )) /* xref_clk2.gpio6_19, spi2_sclk.off */ + BONE_PIN(P9_22, gpio_pd, P9_22A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P9_22B( PIN_OUTPUT | MUX_MODE15 )) /* xref_clk2.gpio6_19, spi2_sclk.off */ + BONE_PIN(P9_22, timer, P9_22A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE10) P9_22B( PIN_OUTPUT | MUX_MODE15 )) /* xref_clk2.timer15, spi2_sclk.off */ + + /* P9_22b (ball A26) gpio7_14 */ + BONE_PIN(P9_22, spi_sclk, P9_22B( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE0) P9_22A( PIN_OUTPUT | MUX_MODE15 )) /* spi2_sclk.spi2_sclk, xref_clk2.off */ + BONE_PIN(P9_22, uart, P9_22B( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE1) P9_22A( PIN_OUTPUT | MUX_MODE15 )) /* spi2_sclk.uart3_rxd, xref_clk2.off */ + + /* P9_22 dummy nodes for unavailable bone bus */ + P9_22_i2c_pin: pinmux_P9_22_default_pin { pinctrl-single,pins = < /* To avoid FDT_ERROR on BBAI when using BONE-I2C2A-00A0.dtbo */ + /* Default pinmux configuration of P9_22 (P9_22_default_pin) */ + P9_22A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P9_22B( PIN_OUTPUT | MUX_MODE15) >; }; /* xref_clk2.gpio6_19, spi2_sclk.off */ + P9_22_pwm_pin: pinmux_P9_22_default_pin { pinctrl-single,pins = < /* To avoid FDT_ERROR on BBAI when using BONE-PWM0-00A0.dtbo */ + /* Default pinmux configuration of P9_22 (P9_22_default_pin) */ + P9_22A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P9_22B( PIN_OUTPUT | MUX_MODE15) >; }; /* xref_clk2.gpio6_19, spi2_sclk.off */ + + /* P9_23 (ball A22) gpio7_11 */ + BONE_PIN(P9_23, default, P9_23( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* spi1_cs1.gpio7_11 */ + BONE_PIN(P9_23, gpio, P9_23( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* spi1_cs1.gpio7_11 */ + BONE_PIN(P9_23, gpio_pu, P9_23( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* spi1_cs1.gpio7_11 */ + BONE_PIN(P9_23, gpio_pd, P9_23( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* spi1_cs1.gpio7_11 */ + BONE_PIN(P9_23, spi_cs, P9_23( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3 )) /* spi1_cs1.spi2_cs1 */ + + /* P9_24 (ball F20) gpio6_15*/ + BONE_PIN(P9_24, default, P9_24( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* gpio6_15.gpio6_15 */ + BONE_PIN(P9_24, gpio, P9_24( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* gpio6_15.gpio6_15 */ + BONE_PIN(P9_24, gpio_pu, P9_24( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* gpio6_15.gpio6_15 */ + BONE_PIN(P9_24, gpio_pd, P9_24( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* gpio6_15.gpio6_15 */ + BONE_PIN(P9_24, uart, P9_24( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3 )) /* gpio6_15.uart10_txd */ + BONE_PIN(P9_24, can, P9_24( PIN_INPUT_PULLUP | MUX_MODE2 )) /* gpio6_15.dcan2_rx */ + BONE_PIN(P9_24, i2c, P9_24( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE9 )) /* gpio6_15.i2c3_scl */ + + /* P9_25 (ball D18) gpio6_17 */ + BONE_PIN(P9_25, default, P9_25( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* xref_clk0.gpio6_17 */ + BONE_PIN(P9_25, gpio, P9_25( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* xref_clk0.gpio6_17 */ + BONE_PIN(P9_25, gpio_pu, P9_25( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* xref_clk0.gpio6_17 */ + BONE_PIN(P9_25, gpio_pd, P9_25( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* xref_clk0.gpio6_17 */ + BONE_PIN(P9_25, pruout, P9_25( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* xref_clk0.pr2_pru1_gpo5 */ + BONE_PIN(P9_25, pruin, P9_25( PIN_INPUT | MUX_MODE12 )) /* xref_clk0.pr2_pru1_gpi5 */ + BONE_PIN(P9_25, mcasp, P9_25( PIN_INPUT_PULLDOWN | MUX_MODE3 )) /* xref_clk0.mcasp1_ahclkx */ + + /* P9_25 dummy nodes for unavailable bone bus pins*/ + P9_25_eqep_pin: pinmux_P9_25_eqep_pin { pinctrl-single,pins = < /* To avoid FDT_ERROR on BBAI when using BONE-EQEP0.dtbo */ + /* Default pinmux configuration of P9_25 (P9_25_default_pin) */ + P9_25( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) >; }; /* xref_clk0.gpio6_17 */ + + /* P9_26a (ball E21) gpio6_14 */ + BONE_PIN(P9_26, default, P9_26A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P9_26B( PIN_OUTPUT | MUX_MODE15 )) /* gpio6_14.gpio6_14, vin1a_d20.off */ + BONE_PIN(P9_26, gpio, P9_26A( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P9_26B( PIN_OUTPUT | MUX_MODE15 )) /* gpio6_14.gpio6_14, vin1a_d20.off */ + BONE_PIN(P9_26, gpio_pu, P9_26A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P9_26B( PIN_OUTPUT | MUX_MODE15 )) /* gpio6_14.gpio6_14, vin1a_d20.off */ + BONE_PIN(P9_26, gpio_pd, P9_26A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P9_26B( PIN_OUTPUT | MUX_MODE15 )) /* gpio6_14.gpio6_14, vin1a_d20.off */ + BONE_PIN(P9_26, uart, P9_26A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) P9_26B( PIN_OUTPUT | MUX_MODE15 )) /* gpio6_14.uart10_rxd, vin1a_d20.off */ + BONE_PIN(P9_26, can, P9_26A( PIN_OUTPUT_PULLUP | MUX_MODE2) P9_26B( PIN_OUTPUT | MUX_MODE15 )) /* gpio6_14.dcan2_tx, vin1a_d20.off */ + BONE_PIN(P9_26, i2c, P9_26A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE9) P9_26B( PIN_OUTPUT | MUX_MODE15 )) /* gpio6_14.i2c3_sda, vin1a_d20.off */ + + /* P9_26b (ball AE2) gpio3_24 */ + BONE_PIN(P9_26, pruout, P9_26B( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13) P9_26A( PIN_OUTPUT | MUX_MODE15 )) /* vin1a_d20.pr1_pru0_gpo17, gpio6_14.off */ + BONE_PIN(P9_26, pruin, P9_26B( PIN_INPUT | MUX_MODE12) P9_26A( PIN_OUTPUT | MUX_MODE15 )) /* vin1a_d20.pr1_pru0_gpi17, gpio6_14.off */ + + /* P9_27a (ball C3) gpio4_15 */ + BONE_PIN(P9_27, default, P9_27A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P9_27B( PIN_OUTPUT | MUX_MODE15 )) /* vin2a_d14.gpio4_15, mcasp1_fsr.off */ + BONE_PIN(P9_27, gpio, P9_27A( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P9_27B( PIN_OUTPUT | MUX_MODE15 )) /* vin2a_d14.gpio4_15, mcasp1_fsr.off */ + BONE_PIN(P9_27, gpio_pu, P9_27A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P9_27B( PIN_OUTPUT | MUX_MODE15 )) /* vin2a_d14.gpio4_15, mcasp1_fsr.off */ + BONE_PIN(P9_27, gpio_pd, P9_27A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P9_27B( PIN_OUTPUT | MUX_MODE15 )) /* vin2a_d14.gpio4_15, mcasp1_fsr.off */ + BONE_PIN(P9_27, eqep, P9_27A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE10) P9_27B( PIN_OUTPUT | MUX_MODE15 )) /* vin2a_d14.eQEP3B_in, mcasp1_fsr.off */ + BONE_PIN(P9_27, pruout, P9_27A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13) P9_27B( PIN_OUTPUT | MUX_MODE15 )) /* vin2a_d14.pr1_pru1_gpo11, mcasp1_fsr.off */ + BONE_PIN(P9_27, pruin, P9_27A( PIN_INPUT | MUX_MODE12) P9_27B( PIN_OUTPUT | MUX_MODE15 )) /* vin2a_d14.pr1_pru1_gpi11, mcasp1_fsr.off */ + + /* P9_27b (ball J14) gpio5_1 */ + BONE_PIN(P9_27, mcasp, P9_27B( PIN_INPUT_PULLDOWN | MUX_MODE0) P9_27A( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_fsr.mcasp1_fsr, vin2a_d14.off */ + + /* P9_28 (ball A12) gpio4_17 */ + BONE_PIN(P9_28, default, P9_28( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* mcasp1_axr11.gpio4_17 */ + BONE_PIN(P9_28, gpio, P9_28( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* mcasp1_axr11.gpio4_17 */ + BONE_PIN(P9_28, gpio_pu, P9_28( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* mcasp1_axr11.gpio4_17 */ + BONE_PIN(P9_28, gpio_pd, P9_28( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* mcasp1_axr11.gpio4_17 */ + BONE_PIN(P9_28, spi_cs, P9_28( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3 )) /* mcasp1_axr11.spi3_cs0 */ + BONE_PIN(P9_28, pruout, P9_28( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* mcasp1_axr11.pr2_pru1_gpo13 */ + BONE_PIN(P9_28, pruin, P9_28( PIN_INPUT | MUX_MODE12 )) /* mcasp1_axr11.pr2_pru1_gpi13 */ + BONE_PIN(P9_28, mcasp, P9_28( PIN_INPUT_PULLDOWN | MUX_MODE0 )) /* mcasp1_axr11.mcasp1_axr11 */ + + /* P9_29a (ball A11) gpio5_11*/ + BONE_PIN(P9_29, default, P9_29A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P9_29B( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_axr9.gpio5_11, mcasp1_fsx.off */ + BONE_PIN(P9_29, gpio, P9_29A( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P9_29B( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_axr9.gpio5_11, mcasp1_fsx.off */ + BONE_PIN(P9_29, gpio_pu, P9_29A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P9_29B( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_axr9.gpio5_11, mcasp1_fsx.off */ + BONE_PIN(P9_29, gpio_pd, P9_29A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P9_29B( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_axr9.gpio5_11, mcasp1_fsx.off */ + BONE_PIN(P9_29, spi, P9_29A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) P9_29B( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_axr9.spi3_d1, mcasp1_fsx.off */ + BONE_PIN(P9_29, pruout, P9_29A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13) P9_29B( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_axr9.pr2_pru1_gpo11, mcasp1_fsx.off */ + BONE_PIN(P9_29, pruin, P9_29A( PIN_INPUT | MUX_MODE12) P9_29B( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_axr9.pr2_pru1_gpi11, mcasp1_fsx.off */ + + /* P9_29b (ball D14) gpio7_30 */ + BONE_PIN(P9_29, mcasp, P9_29B( PIN_INPUT_PULLDOWN | MUX_MODE0) P9_29A( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_fsx.mcasp1_fsx, mcasp1_axr9.off */ + + /* P9_30 (ball B13) gpio5_12*/ + BONE_PIN(P9_30, default, P9_30( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* mcasp1_axr10.gpio5_12 */ + BONE_PIN(P9_30, gpio, P9_30( PIN_OUTPUT | INPUT_EN | MUX_MODE14 )) /* mcasp1_axr10.gpio5_12 */ + BONE_PIN(P9_30, gpio_pu, P9_30( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14 )) /* mcasp1_axr10.gpio5_12 */ + BONE_PIN(P9_30, gpio_pd, P9_30( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14 )) /* mcasp1_axr10.gpio5_12 */ + BONE_PIN(P9_30, spi, P9_30( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3 )) /* mcasp1_axr10.spi3_d0 */ + BONE_PIN(P9_30, pruout, P9_30( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13 )) /* mcasp1_axr10.pr2_pru1_gpo12 */ + BONE_PIN(P9_30, pruin, P9_30( PIN_INPUT | MUX_MODE12 )) /* mcasp1_axr10.pr2_pru1_gpi12 */ + BONE_PIN(P9_30, mcasp, P9_30( PIN_INPUT_PULLDOWN | MUX_MODE0 )) /* mcasp1_axr10.mcasp1_axr10 */ + + /* P9_31a (ball B12) gpio5_10 */ + BONE_PIN(P9_31, default, P9_31A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P9_31B( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_axr8.gpio5_10, mcasp1_aclkx.off */ + BONE_PIN(P9_31, gpio, P9_31A( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P9_31B( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_axr8.gpio5_10, mcasp1_aclkx.off */ + BONE_PIN(P9_31, gpio_pu, P9_31A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P9_31B( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_axr8.gpio5_10, mcasp1_aclkx.off */ + BONE_PIN(P9_31, gpio_pd, P9_31A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P9_31B( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_axr8.gpio5_10, mcasp1_aclkx.off */ + BONE_PIN(P9_31, spi_sclk, P9_31A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) P9_31B( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_axr8.spi3_sclk, mcasp1_aclkx.off */ + BONE_PIN(P9_31, pruout, P9_31A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13) P9_31B( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_axr8.pr2_pru1_gpo10, mcasp1_aclkx.off */ + BONE_PIN(P9_31, pruin, P9_31A( PIN_INPUT | MUX_MODE12) P9_31B( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_axr8.pr2_pru1_gpi10, mcasp1_aclkx.off */ + + /* P9_31b (ball C14) gpio7_31*/ + BONE_PIN(P9_31, mcasp, P9_31B( PIN_INPUT_PULLDOWN | MUX_MODE0) P9_31A( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_aclkx.mcasp1_aclkx, mcasp1_axr8.off */ + + /* P9_32 VADC */ + + /* P9_33 AIN4*/ + + /* P9_34 AGND */ + + /* P9_35 AIN6 */ + + /* P9_36 AIN5 */ + + /* P9_37 AIN2 */ + + /* P9_38 AIN3*/ + + /* P9_39 AIN0*/ + + /* P9_40 AIN1*/ + + /* P9_41a (ball C23) gpio6_20 */ + BONE_PIN(P9_41, default, P9_41A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P9_41B( PIN_OUTPUT | MUX_MODE15 )) /* xref_clk3.gpio6_20, vin2a_d6.off */ + BONE_PIN(P9_41, gpio, P9_41A( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P9_41B( PIN_OUTPUT | MUX_MODE15 )) /* xref_clk3.gpio6_20, vin2a_d6.off */ + BONE_PIN(P9_41, gpio_pu, P9_41A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P9_41B( PIN_OUTPUT | MUX_MODE15 )) /* xref_clk3.gpio6_20, vin2a_d6.off */ + BONE_PIN(P9_41, gpio_pd, P9_41A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P9_41B( PIN_OUTPUT | MUX_MODE15 )) /* xref_clk3.gpio6_20, vin2a_d6.off */ + + /* P9_41b (ball C1) gpio4_7 */ + BONE_PIN(P9_41, pruout, P9_41B( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13) P9_41A( PIN_OUTPUT | MUX_MODE15 )) /* vin2a_d6.pr1_pru1_gpo3, xref_clk3.off */ + BONE_PIN(P9_41, pruin, P9_41B( PIN_INPUT | MUX_MODE12) P9_41A( PIN_OUTPUT | MUX_MODE15 )) /* vin2a_d6.pr1_pru1_gpi3, xref_clk3.off */ + + /* P9_41 dummy nodes for unavailable bone bus pins*/ + P9_41_eqep_pin: pinmux_P9_41_eqep_pin { pinctrl-single,pins = < /* To avoid FDT_ERROR on BBAI when using BONE-EQEP0.dtbo */ + /* Default pinmux configuration of P9_41 (P9_41_default_pin) */ + P9_41A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P9_41B( PIN_OUTPUT | MUX_MODE15) >; }; /* xref_clk3.gpio6_20, vin2a_d6.off */ + + /* P9_42a (ball E14) gpio4_18 */ + BONE_PIN(P9_42, default, P9_42A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P9_42B( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_axr12.gpio4_18, vin2a_d13.off */ + BONE_PIN(P9_42, gpio, P9_42A( PIN_OUTPUT | INPUT_EN | MUX_MODE14) P9_42B( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_axr12.gpio4_18, vin2a_d13.off */ + BONE_PIN(P9_42, gpio_pu, P9_42A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE14) P9_42B( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_axr12.gpio4_18, vin2a_d13.off */ + BONE_PIN(P9_42, gpio_pd, P9_42A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P9_42B( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_axr12.gpio4_18, vin2a_d13.off */ + BONE_PIN(P9_42, spi_cs, P9_42A( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE3) P9_42B( PIN_OUTPUT | MUX_MODE15 )) /* mcasp1_axr12.spi3_cs1, vin2a_d13.off */ + + /* P9_42b (ball C2) gpio4_14*/ + BONE_PIN(P9_42, pruout, P9_42B( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE13) P9_42A( PIN_OUTPUT | MUX_MODE15 )) /* vin2a_d13.pr1_pru1_gpo10, mcasp1_axr12.off */ + BONE_PIN(P9_42, pruin, P9_42B( PIN_INPUT | MUX_MODE12) P9_42A( PIN_OUTPUT | MUX_MODE15 )) /* vin2a_d13.pr1_pru1_gpi10, mcasp1_axr12.off */ + BONE_PIN(P9_42, eqep, P9_42B( PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE10) P9_42A( PIN_OUTPUT | MUX_MODE15 )) /* vin2a_d13.eQEP3A_in, mcasp1_axr12.off */ + + /* P9_42 dummy nodes for unavailable bone bus */ + P9_42_uart_pin: pinmux_P9_42_default_pin { pinctrl-single,pins = < /* To avoid FDT_ERROR on BBAI when using BONE-UART3-00A0.dtbo */ + /* Default pinmux configuration of P9_42 (P9_42_default_pin) */ + P9_42A( PIN_OUTPUT_PULLDOWN | INPUT_EN | MUX_MODE14) P9_42B( PIN_OUTPUT | MUX_MODE15) >; }; /* mcasp1_axr12.gpio4_18, vin2a_d13.off */ + + /* P9_43 GND */ + + /* P9_44 GND */ + + /* P9_45 GND */ + + /* P9_46 GND */ +}; + +/**********************************************************************/ +/* Pin Multiplex Helpers */ +/* */ +/* These provide userspace runtime pin configuration for the */ +/* BeagleBone cape expansion headers */ +/**********************************************************************/ + +&ocp { + /************************/ + /* P8 Header */ + /************************/ + + /* P8_01 GND */ + + /* P8_02 GND */ + + P8_03_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruin", "pruout"; + pinctrl-0 = <&P8_03_default_pin>; + pinctrl-1 = <&P8_03_gpio_pin>; + pinctrl-2 = <&P8_03_gpio_pu_pin>; + pinctrl-3 = <&P8_03_gpio_pd_pin>; + pinctrl-4 = <&P8_03_pruin_pin>; + pinctrl-5 = <&P8_03_pruout_pin>; + }; + + P8_04_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "ecap_pwm", "pruin", "pruout"; + pinctrl-0 = <&P8_04_default_pin>; + pinctrl-1 = <&P8_04_gpio_pin>; + pinctrl-2 = <&P8_04_gpio_pu_pin>; + pinctrl-3 = <&P8_04_gpio_pd_pin>; + pinctrl-4 = <&P8_04_ecap_pwm_pin>; + pinctrl-5 = <&P8_04_pruin_pin>; + pinctrl-6 = <&P8_04_pruout_pin>; + }; + + P8_05_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruin", "pruout"; + pinctrl-0 = <&P8_05_default_pin>; + pinctrl-1 = <&P8_05_gpio_pin>; + pinctrl-2 = <&P8_05_gpio_pu_pin>; + pinctrl-3 = <&P8_05_gpio_pd_pin>; + pinctrl-4 = <&P8_05_eqep_pin>; + pinctrl-5 = <&P8_05_pruin_pin>; + pinctrl-6 = <&P8_05_pruout_pin>; + }; + + P8_06_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruin", "pruout"; + pinctrl-0 = <&P8_06_default_pin>; + pinctrl-1 = <&P8_06_gpio_pin>; + pinctrl-2 = <&P8_06_gpio_pu_pin>; + pinctrl-3 = <&P8_06_gpio_pd_pin>; + pinctrl-4 = <&P8_06_eqep_pin>; + pinctrl-5 = <&P8_06_pruin_pin>; + pinctrl-6 = <&P8_06_pruout_pin>; + }; + + P8_07_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "timer", "pruin", "pruout"; + pinctrl-0 = <&P8_07_default_pin>; + pinctrl-1 = <&P8_07_gpio_pin>; + pinctrl-2 = <&P8_07_gpio_pu_pin>; + pinctrl-3 = <&P8_07_gpio_pd_pin>; + pinctrl-4 = <&P8_07_timer_pin>; + pinctrl-5 = <&P8_07_pruin_pin>; + pinctrl-6 = <&P8_07_pruout_pin>; + }; + + P8_08_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "timer", "pruin", "pruout"; + pinctrl-0 = <&P8_08_default_pin>; + pinctrl-1 = <&P8_08_gpio_pin>; + pinctrl-2 = <&P8_08_gpio_pu_pin>; + pinctrl-3 = <&P8_08_gpio_pd_pin>; + pinctrl-4 = <&P8_08_timer_pin>; + pinctrl-5 = <&P8_08_pruin_pin>; + pinctrl-6 = <&P8_08_pruout_pin>; + }; + + P8_09_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "timer", "pruin", "pruout"; + pinctrl-0 = <&P8_09_default_pin>; + pinctrl-1 = <&P8_09_gpio_pin>; + pinctrl-2 = <&P8_09_gpio_pu_pin>; + pinctrl-3 = <&P8_09_gpio_pd_pin>; + pinctrl-4 = <&P8_09_timer_pin>; + pinctrl-5 = <&P8_09_pruin_pin>; + pinctrl-6 = <&P8_09_pruout_pin>; + }; + + P8_10_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "timer", "pruin", "pruout"; + pinctrl-0 = <&P8_10_default_pin>; + pinctrl-1 = <&P8_10_gpio_pin>; + pinctrl-2 = <&P8_10_gpio_pu_pin>; + pinctrl-3 = <&P8_10_gpio_pd_pin>; + pinctrl-4 = <&P8_10_timer_pin>; + pinctrl-5 = <&P8_10_pruin_pin>; + pinctrl-6 = <&P8_10_pruout_pin>; + }; + + P8_11_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruin", "pruout"; + pinctrl-0 = <&P8_11_default_pin>; + pinctrl-1 = <&P8_11_gpio_pin>; + pinctrl-2 = <&P8_11_gpio_pu_pin>; + pinctrl-3 = <&P8_11_gpio_pd_pin>; + pinctrl-4 = <&P8_11_eqep_pin>; + pinctrl-5 = <&P8_11_pruin_pin>; + pinctrl-6 = <&P8_11_pruout_pin>; + }; + + P8_12_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruin", "pruout"; + pinctrl-0 = <&P8_12_default_pin>; + pinctrl-1 = <&P8_12_gpio_pin>; + pinctrl-2 = <&P8_12_gpio_pu_pin>; + pinctrl-3 = <&P8_12_gpio_pd_pin>; + pinctrl-4 = <&P8_12_eqep_pin>; + pinctrl-5 = <&P8_12_pruin_pin>; + pinctrl-6 = <&P8_12_pruout_pin>; + }; + + P8_13_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm", "pruin", "pruout"; + pinctrl-0 = <&P8_13_default_pin>; + pinctrl-1 = <&P8_13_gpio_pin>; + pinctrl-2 = <&P8_13_gpio_pu_pin>; + pinctrl-3 = <&P8_13_gpio_pd_pin>; + pinctrl-4 = <&P8_13_pwm_pin>; + pinctrl-5 = <&P8_13_pruin_pin>; + pinctrl-6 = <&P8_13_pruout_pin>; + }; + + P8_14_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "ecap_pwm", "pruin", "pruout"; + pinctrl-0 = <&P8_14_default_pin>; + pinctrl-1 = <&P8_14_gpio_pin>; + pinctrl-2 = <&P8_14_gpio_pu_pin>; + pinctrl-3 = <&P8_14_gpio_pd_pin>; + pinctrl-4 = <&P8_14_ecap_pwm_pin>; + pinctrl-5 = <&P8_14_pruin_pin>; + pinctrl-6 = <&P8_14_pruout_pin>; + }; + + P8_15_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pru_ecap_pwm", "pruin", "pruout", "ecap_pwm"; + pinctrl-0 = <&P8_15_default_pin>; + pinctrl-1 = <&P8_15_gpio_pin>; + pinctrl-2 = <&P8_15_gpio_pu_pin>; + pinctrl-3 = <&P8_15_gpio_pd_pin>; + pinctrl-4 = <&P8_15_pru_ecap_pwm_pin>; + pinctrl-5 = <&P8_15_pruin_pin>; + pinctrl-6 = <&P8_15_pruout_pin>; + pinctrl-7 = <&P8_15_ecap_pwm_pin>; + }; + + P8_16_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruin", "pruout"; + pinctrl-0 = <&P8_16_default_pin>; + pinctrl-1 = <&P8_16_gpio_pin>; + pinctrl-2 = <&P8_16_gpio_pu_pin>; + pinctrl-3 = <&P8_16_gpio_pd_pin>; + pinctrl-4 = <&P8_16_pruin_pin>; + pinctrl-5 = <&P8_16_pruout_pin>; + }; + + P8_17_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruin", "pruout"; + pinctrl-0 = <&P8_17_default_pin>; + pinctrl-1 = <&P8_17_gpio_pin>; + pinctrl-2 = <&P8_17_gpio_pu_pin>; + pinctrl-3 = <&P8_17_gpio_pd_pin>; + pinctrl-4 = <&P8_17_pruin_pin>; + pinctrl-5 = <&P8_17_pruout_pin>; + }; + + P8_18_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruin", "pruout"; + pinctrl-0 = <&P8_18_default_pin>; + pinctrl-1 = <&P8_18_gpio_pin>; + pinctrl-2 = <&P8_18_gpio_pu_pin>; + pinctrl-3 = <&P8_18_gpio_pd_pin>; + pinctrl-4 = <&P8_18_eqep_pin>; + pinctrl-5 = <&P8_18_pruin_pin>; + pinctrl-6 = <&P8_18_pruout_pin>; + }; + + P8_19_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm", "pruin", "pruout"; + pinctrl-0 = <&P8_19_default_pin>; + pinctrl-1 = <&P8_19_gpio_pin>; + pinctrl-2 = <&P8_19_gpio_pu_pin>; + pinctrl-3 = <&P8_19_gpio_pd_pin>; + pinctrl-4 = <&P8_19_pwm_pin>; + pinctrl-5 = <&P8_19_pruin_pin>; + pinctrl-6 = <&P8_19_pruout_pin>; + }; + + P8_20_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin", "ecap-pwm"; + pinctrl-0 = <&P8_20_default_pin>; + pinctrl-1 = <&P8_20_gpio_pin>; + pinctrl-2 = <&P8_20_gpio_pu_pin>; + pinctrl-3 = <&P8_20_gpio_pd_pin>; + pinctrl-4 = <&P8_20_pruout_pin>; + pinctrl-5 = <&P8_20_pruin_pin>; + pinctrl-6 = <&P8_20_ecap_pwm_pin>; + }; + + P8_21_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_21_default_pin>; + pinctrl-1 = <&P8_21_gpio_pin>; + pinctrl-2 = <&P8_21_gpio_pu_pin>; + pinctrl-3 = <&P8_21_gpio_pd_pin>; + pinctrl-4 = <&P8_21_pruout_pin>; + pinctrl-5 = <&P8_21_pruin_pin>; + }; + + P8_22_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_22_default_pin>; + pinctrl-1 = <&P8_22_gpio_pin>; + pinctrl-2 = <&P8_22_gpio_pu_pin>; + pinctrl-3 = <&P8_22_gpio_pd_pin>; + pinctrl-4 = <&P8_22_pruout_pin>; + pinctrl-5 = <&P8_22_pruin_pin>; + }; + + P8_23_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_23_default_pin>; + pinctrl-1 = <&P8_23_gpio_pin>; + pinctrl-2 = <&P8_23_gpio_pu_pin>; + pinctrl-3 = <&P8_23_gpio_pd_pin>; + pinctrl-4 = <&P8_23_pruout_pin>; + pinctrl-5 = <&P8_23_pruin_pin>; + }; + + P8_24_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_24_default_pin>; + pinctrl-1 = <&P8_24_gpio_pin>; + pinctrl-2 = <&P8_24_gpio_pu_pin>; + pinctrl-3 = <&P8_24_gpio_pd_pin>; + pinctrl-4 = <&P8_24_pruout_pin>; + pinctrl-5 = <&P8_24_pruin_pin>; + }; + + P8_25_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_25_default_pin>; + pinctrl-1 = <&P8_25_gpio_pin>; + pinctrl-2 = <&P8_25_gpio_pu_pin>; + pinctrl-3 = <&P8_25_gpio_pd_pin>; + pinctrl-4 = <&P8_25_pruout_pin>; + pinctrl-5 = <&P8_25_pruin_pin>; + }; + + P8_26_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "ecap_pwm", "pruout", "pruin"; + pinctrl-0 = <&P8_26_default_pin>; + pinctrl-1 = <&P8_26_gpio_pin>; + pinctrl-2 = <&P8_26_gpio_pu_pin>; + pinctrl-3 = <&P8_26_gpio_pd_pin>; + pinctrl-4 = <&P8_26_ecap_pwm_pin>; + pinctrl-5 = <&P8_26_pruout_pin>; + pinctrl-6 = <&P8_26_pruin_pin>; + }; + + P8_27_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_27_default_pin>; + pinctrl-1 = <&P8_27_gpio_pin>; + pinctrl-2 = <&P8_27_gpio_pu_pin>; + pinctrl-3 = <&P8_27_gpio_pd_pin>; + pinctrl-4 = <&P8_27_pruout_pin>; + pinctrl-5 = <&P8_27_pruin_pin>; + }; + + P8_28_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_28_default_pin>; + pinctrl-1 = <&P8_28_gpio_pin>; + pinctrl-2 = <&P8_28_gpio_pu_pin>; + pinctrl-3 = <&P8_28_gpio_pd_pin>; + pinctrl-4 = <&P8_28_pruout_pin>; + pinctrl-5 = <&P8_28_pruin_pin>; + }; + + P8_29_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_29_default_pin>; + pinctrl-1 = <&P8_29_gpio_pin>; + pinctrl-2 = <&P8_29_gpio_pu_pin>; + pinctrl-3 = <&P8_29_gpio_pd_pin>; + pinctrl-4 = <&P8_29_pruout_pin>; + pinctrl-5 = <&P8_29_pruin_pin>; + }; + + P8_30_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_30_default_pin>; + pinctrl-1 = <&P8_30_gpio_pin>; + pinctrl-2 = <&P8_30_gpio_pu_pin>; + pinctrl-3 = <&P8_30_gpio_pd_pin>; + pinctrl-4 = <&P8_30_pruout_pin>; + pinctrl-5 = <&P8_30_pruin_pin>; + }; + + P8_31_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart", "pruout", "pruin"; + pinctrl-0 = <&P8_31_default_pin>; + pinctrl-1 = <&P8_31_gpio_pin>; + pinctrl-2 = <&P8_31_gpio_pu_pin>; + pinctrl-3 = <&P8_31_gpio_pd_pin>; + pinctrl-4 = <&P8_31_uart_pin>; + pinctrl-5 = <&P8_31_pruout_pin>; + pinctrl-6 = <&P8_31_pruin_pin>; + }; + + P8_32_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_32_default_pin>; + pinctrl-1 = <&P8_32_gpio_pin>; + pinctrl-2 = <&P8_32_gpio_pu_pin>; + pinctrl-3 = <&P8_32_gpio_pd_pin>; + pinctrl-4 = <&P8_32_pruout_pin>; + pinctrl-5 = <&P8_32_pruin_pin>; + }; + + P8_33_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout", "pruin"; + pinctrl-0 = <&P8_33_default_pin>; + pinctrl-1 = <&P8_33_gpio_pin>; + pinctrl-2 = <&P8_33_gpio_pu_pin>; + pinctrl-3 = <&P8_33_gpio_pd_pin>; + pinctrl-4 = <&P8_33_eqep_pin>; + pinctrl-5 = <&P8_33_pruout_pin>; + pinctrl-6 = <&P8_33_pruin_pin>; + }; + + P8_34_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P8_34_default_pin>; + pinctrl-1 = <&P8_34_gpio_pin>; + pinctrl-2 = <&P8_34_gpio_pu_pin>; + pinctrl-3 = <&P8_34_gpio_pd_pin>; + pinctrl-4 = <&P8_34_pwm_pin>; + pinctrl-5 = <&P8_34_pruout_pin>; + pinctrl-6 = <&P8_34_pruin_pin>; + }; + + P8_35_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout", "pruin"; + pinctrl-0 = <&P8_35_default_pin>; + pinctrl-1 = <&P8_35_gpio_pin>; + pinctrl-2 = <&P8_35_gpio_pu_pin>; + pinctrl-3 = <&P8_35_gpio_pd_pin>; + pinctrl-4 = <&P8_35_eqep_pin>; + pinctrl-5 = <&P8_35_pruout_pin>; + pinctrl-6 = <&P8_35_pruin_pin>; + }; + + P8_36_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P8_36_default_pin>; + pinctrl-1 = <&P8_36_gpio_pin>; + pinctrl-2 = <&P8_36_gpio_pu_pin>; + pinctrl-3 = <&P8_36_gpio_pd_pin>; + pinctrl-4 = <&P8_36_pwm_pin>; + pinctrl-5 = <&P8_36_pruout_pin>; + pinctrl-6 = <&P8_36_pruin_pin>; + }; + + P8_37_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart", "pruout", "pruin"; + pinctrl-0 = <&P8_37_default_pin>; + pinctrl-1 = <&P8_37_gpio_pin>; + pinctrl-2 = <&P8_37_gpio_pu_pin>; + pinctrl-3 = <&P8_37_gpio_pd_pin>; + pinctrl-4 = <&P8_37_uart_pin>; + pinctrl-5 = <&P8_37_pruout_pin>; + pinctrl-6 = <&P8_37_pruin_pin>; + }; + + P8_38_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart", "pruout", "pruin"; + pinctrl-0 = <&P8_38_default_pin>; + pinctrl-1 = <&P8_38_gpio_pin>; + pinctrl-2 = <&P8_38_gpio_pu_pin>; + pinctrl-3 = <&P8_38_gpio_pd_pin>; + pinctrl-4 = <&P8_38_uart_pin>; + pinctrl-5 = <&P8_38_pruout_pin>; + pinctrl-6 = <&P8_38_pruin_pin>; + }; + + P8_39_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_39_default_pin>; + pinctrl-1 = <&P8_39_gpio_pin>; + pinctrl-2 = <&P8_39_gpio_pu_pin>; + pinctrl-3 = <&P8_39_gpio_pd_pin>; + pinctrl-4 = <&P8_39_pruout_pin>; + pinctrl-5 = <&P8_39_pruin_pin>; + }; + + P8_40_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_40_default_pin>; + pinctrl-1 = <&P8_40_gpio_pin>; + pinctrl-2 = <&P8_40_gpio_pu_pin>; + pinctrl-3 = <&P8_40_gpio_pd_pin>; + pinctrl-4 = <&P8_40_pruout_pin>; + pinctrl-5 = <&P8_40_pruin_pin>; + }; + + P8_41_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_41_default_pin>; + pinctrl-1 = <&P8_41_gpio_pin>; + pinctrl-2 = <&P8_41_gpio_pu_pin>; + pinctrl-3 = <&P8_41_gpio_pd_pin>; + pinctrl-4 = <&P8_41_pruout_pin>; + pinctrl-5 = <&P8_41_pruin_pin>; + }; + + P8_42_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_42_default_pin>; + pinctrl-1 = <&P8_42_gpio_pin>; + pinctrl-2 = <&P8_42_gpio_pu_pin>; + pinctrl-3 = <&P8_42_gpio_pd_pin>; + pinctrl-4 = <&P8_42_pruout_pin>; + pinctrl-5 = <&P8_42_pruin_pin>; + }; + + P8_43_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_43_default_pin>; + pinctrl-1 = <&P8_43_gpio_pin>; + pinctrl-2 = <&P8_43_gpio_pu_pin>; + pinctrl-3 = <&P8_43_gpio_pd_pin>; + pinctrl-4 = <&P8_43_pruout_pin>; + pinctrl-5 = <&P8_43_pruin_pin>; + }; + + P8_44_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_44_default_pin>; + pinctrl-1 = <&P8_44_gpio_pin>; + pinctrl-2 = <&P8_44_gpio_pu_pin>; + pinctrl-3 = <&P8_44_gpio_pd_pin>; + pinctrl-4 = <&P8_44_pruout_pin>; + pinctrl-5 = <&P8_44_pruin_pin>; + }; + + P8_45_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_45_default_pin>; + pinctrl-1 = <&P8_45_gpio_pin>; + pinctrl-2 = <&P8_45_gpio_pu_pin>; + pinctrl-3 = <&P8_45_gpio_pd_pin>; + pinctrl-4 = <&P8_45_pruout_pin>; + pinctrl-5 = <&P8_45_pruin_pin>; + }; + + P8_46_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P8_46_default_pin>; + pinctrl-1 = <&P8_46_gpio_pin>; + pinctrl-2 = <&P8_46_gpio_pu_pin>; + pinctrl-3 = <&P8_46_gpio_pd_pin>; + pinctrl-4 = <&P8_46_pruout_pin>; + pinctrl-5 = <&P8_46_pruin_pin>; + }; + + /************************/ + /* P9 Header */ + /************************/ + + /* P9_01 GND */ + + /* P9_02 GND */ + + /* P9_03 3V3 */ + + /* P9_04 3V3 */ + + /* P9_05 VDD_5V */ + + /* P9_06 VDD_5V */ + + /* P9_07 SYS_5V */ + + /* P9_08 SYS_5V */ + + /* P9_09 PWR_BUT */ + + /* P9_10 RSTn */ + + P9_11_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart", "pruout", "pruin"; + pinctrl-0 = <&P9_11_default_pin>; + pinctrl-1 = <&P9_11_gpio_pin>; + pinctrl-2 = <&P9_11_gpio_pu_pin>; + pinctrl-3 = <&P9_11_gpio_pd_pin>; + pinctrl-4 = <&P9_11_uart_pin>; + pinctrl-5 = <&P9_11_pruout_pin>; + pinctrl-6 = <&P9_11_pruin_pin>; + }; + + P9_12_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "mcasp"; + pinctrl-0 = <&P9_12_default_pin>; + pinctrl-1 = <&P9_12_gpio_pin>; + pinctrl-2 = <&P9_12_gpio_pu_pin>; + pinctrl-3 = <&P9_12_gpio_pd_pin>; + pinctrl-4 = <&P9_12_mcasp_pin>; + }; + + P9_13_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart", "pruout", "pruin"; + pinctrl-0 = <&P9_13_default_pin>; + pinctrl-1 = <&P9_13_gpio_pin>; + pinctrl-2 = <&P9_13_gpio_pu_pin>; + pinctrl-3 = <&P9_13_gpio_pd_pin>; + pinctrl-4 = <&P9_13_uart_pin>; + pinctrl-5 = <&P9_13_pruout_pin>; + pinctrl-6 = <&P9_13_pruin_pin>; + }; + + P9_14_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P9_14_default_pin>; + pinctrl-1 = <&P9_14_gpio_pin>; + pinctrl-2 = <&P9_14_gpio_pu_pin>; + pinctrl-3 = <&P9_14_gpio_pd_pin>; + pinctrl-4 = <&P9_14_pwm_pin>; + pinctrl-5 = <&P9_14_pruout_pin>; + pinctrl-6 = <&P9_14_pruin_pin>; + }; + + P9_15_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout", "pruin"; + pinctrl-0 = <&P9_15_default_pin>; + pinctrl-1 = <&P9_15_gpio_pin>; + pinctrl-2 = <&P9_15_gpio_pu_pin>; + pinctrl-3 = <&P9_15_gpio_pd_pin>; + pinctrl-4 = <&P9_15_eqep_pin>; + pinctrl-5 = <&P9_15_pruout_pin>; + pinctrl-6 = <&P9_15_pruin_pin>; + }; + + P9_16_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm", "pruout", "pruin"; + pinctrl-0 = <&P9_16_default_pin>; + pinctrl-1 = <&P9_16_gpio_pin>; + pinctrl-2 = <&P9_16_gpio_pu_pin>; + pinctrl-3 = <&P9_16_gpio_pd_pin>; + pinctrl-4 = <&P9_16_pwm_pin>; + pinctrl-5 = <&P9_16_pruout_pin>; + pinctrl-6 = <&P9_16_pruin_pin>; + }; + + P9_17_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_cs", "i2c", "uart", "pruout", "pruin"; + pinctrl-0 = <&P9_17_default_pin>; + pinctrl-1 = <&P9_17_gpio_pin>; + pinctrl-2 = <&P9_17_gpio_pu_pin>; + pinctrl-3 = <&P9_17_gpio_pd_pin>; + pinctrl-4 = <&P9_17_spi_cs_pin>; + pinctrl-5 = <&P9_17_i2c_pin>; + pinctrl-6 = <&P9_17_pru_uart_pin>; + pinctrl-7 = <&P9_17_pruout_pin>; + pinctrl-8 = <&P9_17_pruin_pin>; + }; + + P9_18_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi", "i2c", "uart", "pruout", "pruin"; + pinctrl-0 = <&P9_18_default_pin>; + pinctrl-1 = <&P9_18_gpio_pin>; + pinctrl-2 = <&P9_18_gpio_pu_pin>; + pinctrl-3 = <&P9_18_gpio_pd_pin>; + pinctrl-4 = <&P9_18_spi_pin>; + pinctrl-5 = <&P9_18_i2c_pin>; + pinctrl-6 = <&P9_18_pru_uart_pin>; + pinctrl-7 = <&P9_18_pruout_pin>; + pinctrl-8 = <&P9_18_pruin_pin>; + }; + + P9_19_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "i2c", "eqep", "pruout", "pruin"; + pinctrl-0 = <&P9_19_default_pin>; + pinctrl-1 = <&P9_19_gpio_pin>; + pinctrl-2 = <&P9_19_gpio_pu_pin>; + pinctrl-3 = <&P9_19_gpio_pd_pin>; + pinctrl-4 = <&P9_19_i2c_pin>; + pinctrl-5 = <&P9_19_eqep_pin>; + pinctrl-6 = <&P9_19_pruout_pin>; + pinctrl-7 = <&P9_19_pruin_pin>; + }; + + P9_20_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "i2c", "pruout", "pruin"; + pinctrl-0 = <&P9_20_default_pin>; + pinctrl-1 = <&P9_20_gpio_pin>; + pinctrl-2 = <&P9_20_gpio_pu_pin>; + pinctrl-3 = <&P9_20_gpio_pd_pin>; + pinctrl-4 = <&P9_20_i2c_pin>; + pinctrl-5 = <&P9_20_pruout_pin>; + pinctrl-6 = <&P9_20_pruin_pin>; + }; + + P9_21_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi", "uart", "eqep"; + pinctrl-0 = <&P9_21_default_pin>; + pinctrl-1 = <&P9_21_gpio_pin>; + pinctrl-2 = <&P9_21_gpio_pu_pin>; + pinctrl-3 = <&P9_21_gpio_pd_pin>; + pinctrl-4 = <&P9_21_spi_pin>; + pinctrl-5 = <&P9_21_uart_pin>; + pinctrl-6 = <&P9_21_eqep_pin>; + }; + + P9_22_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_sclk", "uart"; + pinctrl-0 = <&P9_22_default_pin>; + pinctrl-1 = <&P9_22_gpio_pin>; + pinctrl-2 = <&P9_22_gpio_pu_pin>; + pinctrl-3 = <&P9_22_gpio_pd_pin>; + pinctrl-4 = <&P9_22_spi_sclk_pin>; + pinctrl-5 = <&P9_22_uart_pin>; + }; + + P9_23_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd"; + pinctrl-0 = <&P9_23_default_pin>; + pinctrl-1 = <&P9_23_gpio_pin>; + pinctrl-2 = <&P9_23_gpio_pu_pin>; + pinctrl-3 = <&P9_23_gpio_pd_pin>; + }; + + P9_24_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart", "can", "i2c"; + pinctrl-0 = <&P9_24_default_pin>; + pinctrl-1 = <&P9_24_gpio_pin>; + pinctrl-2 = <&P9_24_gpio_pu_pin>; + pinctrl-3 = <&P9_24_gpio_pd_pin>; + pinctrl-4 = <&P9_24_uart_pin>; + pinctrl-5 = <&P9_24_can_pin>; + pinctrl-6 = <&P9_24_i2c_pin>; + }; + + P9_25_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin", "mcasp"; + pinctrl-0 = <&P9_25_default_pin>; + pinctrl-1 = <&P9_25_gpio_pin>; + pinctrl-2 = <&P9_25_gpio_pu_pin>; + pinctrl-3 = <&P9_25_gpio_pd_pin>; + pinctrl-4 = <&P9_25_pruout_pin>; + pinctrl-5 = <&P9_25_pruin_pin>; + pinctrl-6 = <&P9_25_mcasp_pin>; + }; + + P9_26_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart", "can", "i2c", "pruout", "pruin"; + pinctrl-0 = <&P9_26_default_pin>; + pinctrl-1 = <&P9_26_gpio_pin>; + pinctrl-2 = <&P9_26_gpio_pu_pin>; + pinctrl-3 = <&P9_26_gpio_pd_pin>; + pinctrl-4 = <&P9_26_uart_pin>; + pinctrl-5 = <&P9_26_can_pin>; + pinctrl-6 = <&P9_26_i2c_pin>; + pinctrl-7 = <&P9_26_pruout_pin>; + pinctrl-8 = <&P9_26_pruin_pin>; + }; + + P9_27_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "eqep", "pruout", "pruin", "mcasp"; + pinctrl-0 = <&P9_27_default_pin>; + pinctrl-1 = <&P9_27_gpio_pin>; + pinctrl-2 = <&P9_27_gpio_pu_pin>; + pinctrl-3 = <&P9_27_gpio_pd_pin>; + pinctrl-4 = <&P9_27_eqep_pin>; + pinctrl-5 = <&P9_27_pruout_pin>; + pinctrl-6 = <&P9_27_pruin_pin>; + pinctrl-7 = <&P9_27_mcasp_pin>; + }; + + P9_28_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_cs", "pruout", "pruin", "mcasp"; + pinctrl-0 = <&P9_28_default_pin>; + pinctrl-1 = <&P9_28_gpio_pin>; + pinctrl-2 = <&P9_28_gpio_pu_pin>; + pinctrl-3 = <&P9_28_gpio_pd_pin>; + pinctrl-4 = <&P9_28_spi_cs_pin>; + pinctrl-5 = <&P9_28_pruout_pin>; + pinctrl-6 = <&P9_28_pruin_pin>; + pinctrl-7 = <&P9_28_mcasp_pin>; + }; + + P9_29_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi", "pruout", "pruin", "mcasp"; + pinctrl-0 = <&P9_29_default_pin>; + pinctrl-1 = <&P9_29_gpio_pin>; + pinctrl-2 = <&P9_29_gpio_pu_pin>; + pinctrl-3 = <&P9_29_gpio_pd_pin>; + pinctrl-4 = <&P9_29_spi_pin>; + pinctrl-5 = <&P9_29_pruout_pin>; + pinctrl-6 = <&P9_29_pruin_pin>; + pinctrl-7 = <&P9_29_mcasp_pin>; + }; + + P9_30_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi", "pruout", "pruin", "mcasp"; + pinctrl-0 = <&P9_30_default_pin>; + pinctrl-1 = <&P9_30_gpio_pin>; + pinctrl-2 = <&P9_30_gpio_pu_pin>; + pinctrl-3 = <&P9_30_gpio_pd_pin>; + pinctrl-4 = <&P9_30_spi_pin>; + pinctrl-5 = <&P9_30_pruout_pin>; + pinctrl-6 = <&P9_30_pruin_pin>; + pinctrl-7 = <&P9_30_mcasp_pin>; + }; + + P9_31_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_sclk", "pruout", "pruin", "mcasp"; + pinctrl-0 = <&P9_31_default_pin>; + pinctrl-1 = <&P9_31_gpio_pin>; + pinctrl-2 = <&P9_31_gpio_pu_pin>; + pinctrl-3 = <&P9_31_gpio_pd_pin>; + pinctrl-4 = <&P9_31_spi_sclk_pin>; + pinctrl-5 = <&P9_31_pruout_pin>; + pinctrl-6 = <&P9_31_pruin_pin>; + pinctrl-7 = <&P9_31_mcasp_pin>; + }; + + /* P9_32 VADC */ + + /* P9_33 AIN4*/ + + /* P9_34 AGND */ + + /* P9_35 AIN6 */ + + /* P9_36 AIN5 */ + + /* P9_37 AIN2 */ + + /* P9_38 AIN3*/ + + /* P9_39 AIN0*/ + + /* P9_40 AIN1*/ + + P9_41_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin"; + pinctrl-0 = <&P9_41_default_pin>; + pinctrl-1 = <&P9_41_gpio_pin>; + pinctrl-2 = <&P9_41_gpio_pu_pin>; + pinctrl-3 = <&P9_41_gpio_pd_pin>; + pinctrl-4 = <&P9_41_pruout_pin>; + pinctrl-5 = <&P9_41_pruin_pin>; + }; + + P9_42_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi_cs", "pruout", "pruin", "eqep"; + pinctrl-0 = <&P9_42_default_pin>; + pinctrl-1 = <&P9_42_gpio_pin>; + pinctrl-2 = <&P9_42_gpio_pu_pin>; + pinctrl-3 = <&P9_42_gpio_pd_pin>; + pinctrl-4 = <&P9_42_spi_cs_pin>; + pinctrl-5 = <&P9_42_pruout_pin>; + pinctrl-6 = <&P9_42_pruin_pin>; + pinctrl-7 = <&P9_42_eqep_pin>; + }; + + /* P9_43 GND */ + + /* P9_44 GND */ + + /* P9_45 GND */ + + /* P9_46 GND */ + + cape-universal { + compatible = "gpio-of-helper"; + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + + P8_03 { + gpio-name = "P8_03"; + gpio = <&gpio1 24 0>; + input; + dir-changeable; + }; + + P8_04 { + gpio-name = "P8_04"; + gpio = <&gpio1 25 0>; + input; + dir-changeable; + }; + + P8_05 { + gpio-name = "P8_05"; + gpio = <&gpio7 1 0>; + input; + dir-changeable; + }; + + P8_06 { + gpio-name = "P8_06"; + gpio = <&gpio7 2 0>; + input; + dir-changeable; + }; + + P8_07 { + gpio-name = "P8_07"; + gpio = <&gpio6 5 0>; + input; + dir-changeable; + }; + + P8_08 { + gpio-name = "P8_08"; + gpio = <&gpio6 6 0>; + input; + dir-changeable; + }; + + P8_09 { + gpio-name = "P8_09"; + gpio = <&gpio6 18 0>; + input; + dir-changeable; + }; + + P8_10 { + gpio-name = "P8_10"; + gpio = <&gpio6 4 0>; + input; + dir-changeable; + }; + + P8_11 { + gpio-name = "P8_11"; + gpio = <&gpio3 11 0>; + input; + dir-changeable; + }; + + P8_12 { + gpio-name = "P8_12"; + gpio = <&gpio3 10 0>; + input; + dir-changeable; + }; + + P8_13 { + gpio-name = "P8_13"; + gpio = <&gpio4 11 0>; + input; + dir-changeable; + }; + + P8_14 { + gpio-name = "P8_14"; + gpio = <&gpio4 13 0>; + input; + dir-changeable; + }; + + P8_15 { + gpio-name = "P8_15"; + gpio = <&gpio4 3 0>; + input; + dir-changeable; + }; + + P8_16 { + gpio-name = "P8_16"; + gpio = <&gpio4 29 0>; + input; + dir-changeable; + }; + + P8_17 { + gpio-name = "P8_17"; + gpio = <&gpio8 18 0>; + input; + dir-changeable; + }; + + P8_18 { + gpio-name = "P8_18"; + gpio = <&gpio4 9 0>; + input; + dir-changeable; + }; + + P8_19 { + gpio-name = "P8_19"; + gpio = <&gpio4 10 0>; + input; + dir-changeable; + }; + + P8_20 { + gpio-name = "P8_20"; + gpio = <&gpio6 30 0>; + input; + dir-changeable; + }; + + P8_21 { + gpio-name = "P8_21"; + gpio = <&gpio6 29 0>; + input; + dir-changeable; + }; + + P8_22 { + gpio-name = "P8_22"; + gpio = <&gpio1 23 0>; + input; + dir-changeable; + }; + + P8_23 { + gpio-name = "P8_23"; + gpio = <&gpio1 22 0>; + input; + dir-changeable; + }; + + P8_24 { + gpio-name = "P8_24"; + gpio = <&gpio7 0 0>; + input; + dir-changeable; + }; + + P8_25 { + gpio-name = "P8_25"; + gpio = <&gpio6 31 0>; + input; + dir-changeable; + }; + + P8_26 { + gpio-name = "P8_26"; + gpio = <&gpio4 28 0>; + input; + dir-changeable; + }; + + P8_27 { + gpio-name = "P8_27"; + gpio = <&gpio4 23 0>; + input; + dir-changeable; + }; + + P8_28 { + gpio-name = "P8_28"; + gpio = <&gpio4 19 0>; + input; + dir-changeable; + }; + + P8_29 { + gpio-name = "P8_29"; + gpio = <&gpio4 22 0>; + input; + dir-changeable; + }; + + P8_30 { + gpio-name = "P8_30"; + gpio = <&gpio4 20 0>; + input; + dir-changeable; + }; + + P8_31 { + gpio-name = "P8_31"; + gpio = <&gpio8 14 0>; + input; + dir-changeable; + }; + + P8_32 { + gpio-name = "P8_32"; + gpio = <&gpio8 15 0>; + input; + dir-changeable; + }; + + P8_33 { + gpio-name = "P8_33"; + gpio = <&gpio8 13 0>; + input; + dir-changeable; + }; + + P8_34 { + gpio-name = "P8_34"; + gpio = <&gpio8 11 0>; + input; + dir-changeable; + }; + + P8_35 { + gpio-name = "P8_35"; + gpio = <&gpio8 12 0>; + input; + dir-changeable; + }; + + P8_36 { + gpio-name = "P8_36"; + gpio = <&gpio8 10 0>; + input; + dir-changeable; + }; + + P8_37 { + gpio-name = "P8_37"; + gpio = <&gpio8 8 0>; + input; + dir-changeable; + }; + + P8_38 { + gpio-name = "P8_38"; + gpio = <&gpio8 9 0>; + input; + dir-changeable; + }; + + P8_39 { + gpio-name = "P8_39"; + gpio = <&gpio8 6 0>; + input; + dir-changeable; + }; + + P8_40 { + gpio-name = "P8_40"; + gpio = <&gpio8 7 0>; + input; + dir-changeable; + }; + + P8_41 { + gpio-name = "P8_41"; + gpio = <&gpio8 4 0>; + input; + dir-changeable; + }; + + P8_42 { + gpio-name = "P8_42"; + gpio = <&gpio8 5 0>; + input; + dir-changeable; + }; + + P8_43 { + gpio-name = "P8_43"; + gpio = <&gpio8 2 0>; + input; + dir-changeable; + }; + + P8_44 { + gpio-name = "P8_44"; + gpio = <&gpio8 3 0>; + input; + dir-changeable; + }; + + P8_45 { + gpio-name = "P8_45"; + gpio = <&gpio8 0 0>; + input; + dir-changeable; + }; + + P8_46 { + gpio-name = "P8_46"; + gpio = <&gpio8 1 0>; + input; + dir-changeable; + }; + + P9_11 { + gpio-name = "P9_11"; + gpio = <&gpio8 17 0>; + input; + dir-changeable; + }; + + P9_12 { + gpio-name = "P9_12"; + gpio = <&gpio5 0 0>; + input; + dir-changeable; + }; + + P9_13 { + gpio-name = "P9_13"; + gpio = <&gpio6 12 0>; + input; + dir-changeable; + }; + + P9_14 { + gpio-name = "P9_14"; + gpio = <&gpio4 25 0>; + input; + dir-changeable; + }; + + P9_15 { + gpio-name = "P9_15"; + gpio = <&gpio3 12 0>; + input; + dir-changeable; + }; + + P9_16 { + gpio-name = "P9_16"; + gpio = <&gpio4 26 0>; + input; + dir-changeable; + }; + + P9_17 { + gpio-name = "P9_17"; + gpio = <&gpio7 17 0>; + input; + dir-changeable; + }; + + P9_18 { + gpio-name = "P9_18"; + gpio = <&gpio7 16 0>; + input; + dir-changeable; + }; + + P9_19 { + gpio-name = "P9_19"; + gpio = <&gpio7 3 0>; + input; + dir-changeable; + }; + + P9_20 { + gpio-name = "P9_20"; + gpio = <&gpio7 4 0>; + input; + dir-changeable; + }; + + P9_21 { + gpio-name = "P9_21"; + gpio = <&gpio3 3 0>; + input; + dir-changeable; + }; + + P9_22 { + gpio-name = "P9_22"; + gpio = <&gpio6 19 0>; + input; + dir-changeable; + }; + + P9_23 { + gpio-name = "P9_23"; + gpio = <&gpio7 11 0>; + input; + dir-changeable; + }; + + P9_24 { + gpio-name = "P9_24"; + gpio = <&gpio6 15 0>; + input; + dir-changeable; + }; + + P9_25 { + gpio-name = "P9_25"; + gpio = <&gpio6 17 0>; + input; + dir-changeable; + }; + + P9_26 { + gpio-name = "P9_26"; + gpio = <&gpio6 14 0>; + input; + dir-changeable; + }; + + P9_27 { + gpio-name = "P9_27"; + gpio = <&gpio4 15 0>; + input; + dir-changeable; + }; + + P9_28 { + gpio-name = "P9_28"; + gpio = <&gpio4 17 0>; + input; + dir-changeable; + }; + + P9_29 { + gpio-name = "P9_29"; + gpio = <&gpio5 11 0>; + input; + dir-changeable; + }; + + P9_30 { + gpio-name = "P9_30"; + gpio = <&gpio5 12 0>; + input; + dir-changeable; + }; + + P9_31 { + gpio-name = "P9_31"; + gpio = <&gpio5 10 0>; + input; + dir-changeable; + }; + + P9_41 { + gpio-name = "P9_41"; + gpio = <&gpio6 20 0>; + input; + dir-changeable; + }; + + P9_42 { + gpio-name = "P9_42"; + gpio = <&gpio4 18 0>; + input; + dir-changeable; + }; + }; +}; diff --git a/arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi b/arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi index b5ea44497a4f2..c8e25f9c5478b 100644 --- a/arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi +++ b/arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi @@ -641,3 +641,11 @@ &bb2d { status = "okay"; }; + +&pruss1_mdio { + status = "disabled"; +}; + +&pruss2_mdio { + status = "disabled"; +}; diff --git a/arch/arm/boot/dts/am57xx-beagle-x15-revb1.dts b/arch/arm/boot/dts/am57xx-beagle-x15-revb1.dts index 83e174e053c77..0ba920286dfb9 100644 --- a/arch/arm/boot/dts/am57xx-beagle-x15-revb1.dts +++ b/arch/arm/boot/dts/am57xx-beagle-x15-revb1.dts @@ -7,6 +7,11 @@ / { model = "TI AM5728 BeagleBoard-X15 rev B1"; + + chosen { + base_dtb = "am57xx-beagle-x15-revb1.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; }; &tpd12s015 { diff --git a/arch/arm/boot/dts/am57xx-beagle-x15-revc.dts b/arch/arm/boot/dts/am57xx-beagle-x15-revc.dts index 656dd84460d24..9c721c0308f3d 100644 --- a/arch/arm/boot/dts/am57xx-beagle-x15-revc.dts +++ b/arch/arm/boot/dts/am57xx-beagle-x15-revc.dts @@ -7,6 +7,11 @@ / { model = "TI AM5728 BeagleBoard-X15 rev C"; + + chosen { + base_dtb = "am57xx-beagle-x15-revc.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; }; &tpd12s015 { diff --git a/arch/arm/boot/dts/am57xx-beagle-x15.dts b/arch/arm/boot/dts/am57xx-beagle-x15.dts index 0a8b16505ed98..028928f8d43e2 100644 --- a/arch/arm/boot/dts/am57xx-beagle-x15.dts +++ b/arch/arm/boot/dts/am57xx-beagle-x15.dts @@ -8,6 +8,11 @@ / { /* NOTE: This describes the "original" pre-production A2 revision */ model = "TI AM5728 BeagleBoard-X15"; + + chosen { + base_dtb = "am57xx-beagle-x15.dts"; + base_dtb_timestamp = __TIMESTAMP__; + }; }; &tpd12s015 { diff --git a/arch/arm/boot/dts/bbai-bone-buses.dtsi b/arch/arm/boot/dts/bbai-bone-buses.dtsi new file mode 100644 index 0000000000000..93bd7dbb7580d --- /dev/null +++ b/arch/arm/boot/dts/bbai-bone-buses.dtsi @@ -0,0 +1,646 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020 Deepak Khatri + * See Cape Interface Spec page for more info on Bone Buses + * https://elinux.org/Beagleboard:BeagleBone_cape_interface_spec + */ + +#include +#include +#include "am572x-bone-common-univ.dtsi" + +/********/ +/* LEDs */ +/********/ +&{/} { + leds { + pinctrl-names = "default"; + compatible = "gpio-leds"; + + bone_led_P8_03: led_P8_03 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_04: led_P8_04 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_05: led_P8_05 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_06: led_P8_06 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_07: led_P8_07 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_08: led_P8_08 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_09: led_P8_09 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_10: led_P8_10 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_11: led_P8_11 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_12: led_P8_12 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_13: led_P8_13 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_14: led_P8_14 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_15: led_P8_15 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_16: led_P8_16 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_17: led_P8_17 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_18: led_P8_18 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_19: led_P8_19 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_20: led_P8_20 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_21: led_P8_21 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_22: led_P8_22 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_23: led_P8_23 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_24: led_P8_24 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_25: led_P8_25 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_26: led_P8_26 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_27: led_P8_27 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_28: led_P8_28 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_29: led_P8_29 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_30: led_P8_30 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_31: led_P8_31 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_32: led_P8_32 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_33: led_P8_33 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_34: led_P8_34 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_35: led_P8_35 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_36: led_P8_36 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_37: led_P8_37 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_38: led_P8_38 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_39: led_P8_39 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_40: led_P8_40 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_41: led_P8_41 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_42: led_P8_42 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_43: led_P8_43 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_44: led_P8_44 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_45: led_P8_45 { + gpios = ; + status = "disabled"; + }; + + bone_led_P8_46: led_P8_46 { + gpios = ; + status = "disabled"; + }; + + bone_led_P9_11: led_P9_11 { + gpios = ; + status = "disabled"; + }; + + bone_led_P9_12: led_P9_12 { + gpios = ; + status = "disabled"; + }; + + bone_led_P9_13: led_P9_13 { + gpios = ; + status = "disabled"; + }; + + bone_led_P9_14: led_P9_14 { + gpios = ; + status = "disabled"; + }; + + bone_led_P9_15: led_P9_15 { + gpios = ; + status = "disabled"; + }; + + bone_led_P9_16: led_P9_16 { + gpios = ; + status = "disabled"; + }; + + bone_led_P9_17: led_P9_17 { + gpios = ; + status = "disabled"; + }; + + bone_led_P9_18: led_P9_18 { + gpios = ; + status = "disabled"; + }; + + bone_led_P9_19: led_P9_19 { + gpios = ; + status = "disabled"; + }; + + bone_led_P9_20: led_P9_20 { + gpios = ; + status = "disabled"; + }; + + bone_led_P9_21: led_P9_21 { + gpios = ; + status = "disabled"; + }; + + bone_led_P9_22: led_P9_22 { + gpios = ; + status = "disabled"; + }; + + bone_led_P9_23: led_P9_23 { + gpios = ; + status = "disabled"; + }; + + bone_led_P9_24: led_P9_24 { + gpios = ; + status = "disabled"; + }; + + bone_led_P9_25: led_P9_25 { + gpios = ; + status = "disabled"; + }; + + bone_led_P9_26: led_P9_26 { + gpios = ; + status = "disabled"; + }; + + bone_led_P9_27: led_P9_27 { + gpios = ; + status = "disabled"; + }; + + bone_led_P9_28: led_P9_28 { + gpios = ; + status = "disabled"; + }; + + bone_led_P9_29: led_P9_29 { + gpios = ; + status = "disabled"; + }; + + bone_led_P9_30: led_P9_30 { + gpios = ; + status = "disabled"; + }; + + bone_led_P9_31: led_P9_31 { + gpios = ; + status = "disabled"; + }; + + bone_led_P9_41: led_P9_41 { + gpios = ; + status = "disabled"; + }; + + bone_led_P9_42: led_P9_42 { + gpios = ; + status = "disabled"; + }; + }; +}; + +// For dummy refrence when peripheral is not available. +&{/} { + not_available: not_available { + // Use ¬_available phandle when bus not available! + // This node is responsible to create these entries, + // /sys/firmware/devicetree/base/__symbols__/not_available + // /sys/firmware/devicetree/base/not_available + }; +}; + +// For compatible bone pinmuxing +bone_pinmux: &dra7_pmx_core { + +}; + +// UARTs +bone_uart_1: &uart10 { + symlink = "bone/uart/1"; +}; + +bone_uart_2: &uart3 { + symlink = "bone/uart/2"; +}; + +bone_uart_3: ¬_available { + // not available on BBAI +}; + +bone_uart_4: &uart5 { + symlink = "bone/uart/4"; +}; + +bone_uart_5: &uart8 { + symlink = "bone/uart/5"; +}; + +// I2Cs +bone_i2c_1: &i2c5 { + symlink = "bone/i2c/1"; +}; + +bone_i2c_2: &i2c4 { + // Already in use for cape EEPROM reading. + symlink = "bone/i2c/2"; +}; + +bone_i2c_2a: ¬_available { + // Not available on BBAI +}; + +bone_i2c_3: &i2c3 { + symlink = "bone/i2c/3"; +}; + +// SPIs +bone_spi_0: &mcspi2 { + +}; + +bone_spi_1: &mcspi3 { + +}; + +// PWMs +bone_pwm_0: ¬_available { + // Not available on BBAI +}; + +bone_pwm_1: &ehrpwm2 { + +}; + +bone_pwm_2: &ehrpwm1 { + +}; + +// CAN +bone_can_0: ¬_available { + // Not available on BBAI +}; + +bone_can_1: &dcan2 { + +}; + +// PWM_TIMERs +bone_timer_0: &timer10 { + +}; + +bone_timer_1: &timer11 { + +}; + +bone_timer_2: &timer12 { + +}; + +bone_timer_3: &timer13 { + +}; + +bone_timer_4: &timer14 { + +}; + +bone_timer_5: &timer15 { + +}; + +// McASP +bone_mcasp_0: &mcasp1 { + #sound-dai-cells= <0>; + assigned-clocks = <&ipu_clkctrl DRA7_MCASP1_CLKCTRL 24>; + assigned-clock-parents = <&sys_clkin2>; /* 22579200 Hz (see dra7xx-clocks.dtsi) */ + serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */ + 0 0 0 0 + 0 0 0 0 + 0 0 2 1 + 0 0 0 0 + >; +}; + +// eQEP +// bone_eqep_0: &eqep2{ + +// }; + +// bone_eqep_1: &eqep0{ + +// }; + +// bone_eqep_2: &eqep1{ + +// }; + +// ADC +// No internal ADC, STMPE811 Touch Controller is used! +// bone_adc label is created in am5729-beagleboneai.dts + +// Enable all pwmss +&epwmss0 { + status = "okay"; +}; + +&epwmss1 { + status = "okay"; +}; + +&epwmss2 { + status = "okay"; +}; + +// spi_gpio (bit banged SPI) +&{/} { + // For CTAG SW 8ch overlay + bone_spi_gpio_ad193x: bone_spi_gpio_ad193x { + status = "disabled"; + compatible = "spi-gpio"; + #address-cells = <1>; + #size-cells = <0>; + + sck-gpios = ; + mosi-gpios = ; + miso-gpios = ; + cs-gpios = ; + num-chipselects = <1>; + + ad193x: ad193x@0{ + compatible = "analog,ad1938"; + reset-gpio = ; + reg = <0>; //corresponds to cs + spi-max-frequency = <100000>; + }; + }; +}; + +// Bone sound +&ocp { + bone_sound: bone_sound { + bb-device = <1>; //BBx15/BBAI + }; +}; + +// LCD +// Display Sub System (DSS) +&dss { + ports { + #address-cells = <1>; + #size-cells = <0>; + + port { + reg = <0>; + + dpi_out: endpoint { + data-lines = <16>; + remote-endpoint = <&lcd_in>; + }; + }; + }; +}; + +// Touch Controller +&bone_i2c_1 { + status = "okay"; + clock-frequency = <100000>; + #address-cells = <1>; + #size-cells = <0>; + + // Bone Touch controller + bone_polytouch_edt: edt-ft5x06@38 { + status = "disabled"; + compatible = "edt,edt-ft5x06"; + reg = <0x38>; + interrupt-parent = <&gpio6>; + interrupts = <14 2>; + touchscreen-size-y = <480>; + touchscreen-size-x = <272>; + touchscreen-swapped-x-y; + }; +}; + +&{/} { + + // Backlight(s) + // Backlight on pin P9_14 + bone_backlight: backlight { + status = "disabled"; + compatible = "pwm-backlight"; + pwms = <&bone_pwm_1 0 500000 0>; + brightness-levels = < + 0 1 2 3 4 5 6 7 8 9 + 10 11 12 13 14 15 16 17 18 19 + 20 21 22 23 24 25 26 27 28 29 + 30 31 32 33 34 35 36 37 38 39 + 40 41 42 43 44 45 46 47 48 49 + 50 51 52 53 54 55 56 57 58 59 + 60 61 62 63 64 65 66 67 68 69 + 70 71 72 73 74 75 76 77 78 79 + 80 81 82 83 84 85 86 87 88 89 + 90 91 92 93 94 95 96 97 98 99 + 100 + >; + default-brightness-level = <100>; + }; + + // Display(s) + // 4D Systems GEN4-4DCAPE-43CT-CLB Cape + bone_lcd_4d4c: display { + status = "disabled"; + compatible = "qiaodian,qd43003c0-40", "panel-dpi"; + backlight = <&bone_backlight>; + enable-gpios = <&gpio2 5 0>; + label = "lcd"; + + panel-info { + ac-bias = <255>; + ac-bias-intrpt = <0>; + dma-burst-sz = <16>; + bpp = <16>; + fdd = <0x80>; + sync-edge = <0>; + sync-ctrl = <1>; + raster-order = <0>; + fifo-th = <0>; + }; + + panel-timing { + clock-frequency = <9200000>; + hactive = <480>; + vactive = <272>; + hfront-porch = <8>; + hback-porch = <47>; + hsync-len = <41>; + vback-porch = <2>; + vfront-porch = <3>; + vsync-len = <10>; + hsync-active = <0>; + vsync-active = <0>; + de-active = <1>; + pixelclk-active = <0>; + }; + + port { + lcd_in: endpoint { + remote-endpoint = <&dpi_out>; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/bbb-bone-buses.dtsi b/arch/arm/boot/dts/bbb-bone-buses.dtsi new file mode 100644 index 0000000000000..8ccaa352a3ebd --- /dev/null +++ b/arch/arm/boot/dts/bbb-bone-buses.dtsi @@ -0,0 +1,315 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * TODO needs to be merged into am335x-bbb-bone-buses.dtsi and + * tested with BeagleBone Green Wireless (which uses wifi over + * the P8/P9 header).. RCNEE 20221118 + */ + +/* + * Copyright (C) 2020 Deepak Khatri + * See Cape Interface Spec page for more info on Bone Buses + * https://elinux.org/Beagleboard:BeagleBone_cape_interface_spec + */ + +#include +#include +#include "am335x-bone-common-univ.dtsi" + +/********/ +/* LEDs */ +/********/ +&{/} { + leds { + pinctrl-names = "default"; + compatible = "gpio-leds"; + + /* macro: BONE_LED() */ + #define BONE_LED(PX_YY)\ + bone_led_##PX_YY##: led_##PX_YY {\ + status = "disabled";\ + linux,default-trigger = "default-off";\ + gpios = ;\ + }; + + BONE_LED(P8_03) + BONE_LED(P8_04) + BONE_LED(P8_05) + BONE_LED(P8_06) + BONE_LED(P8_07) + BONE_LED(P8_08) + BONE_LED(P8_09) + BONE_LED(P8_10) + BONE_LED(P8_11) + BONE_LED(P8_12) + BONE_LED(P8_13) + BONE_LED(P8_14) + BONE_LED(P8_15) + BONE_LED(P8_16) + BONE_LED(P8_17) + BONE_LED(P8_18) + BONE_LED(P8_19) + BONE_LED(P8_20) + BONE_LED(P8_21) + BONE_LED(P8_22) + BONE_LED(P8_23) + BONE_LED(P8_24) + BONE_LED(P8_25) + BONE_LED(P8_26) + BONE_LED(P8_27) + BONE_LED(P8_28) + BONE_LED(P8_29) + BONE_LED(P8_30) + BONE_LED(P8_31) + BONE_LED(P8_32) + BONE_LED(P8_33) + BONE_LED(P8_34) + BONE_LED(P8_35) + BONE_LED(P8_36) + BONE_LED(P8_37) + BONE_LED(P8_38) + BONE_LED(P8_39) + BONE_LED(P8_40) + BONE_LED(P8_41) + BONE_LED(P8_42) + BONE_LED(P8_43) + BONE_LED(P8_44) + BONE_LED(P8_45) + BONE_LED(P8_46) + + /*P9 header Bone LEDs*/ + BONE_LED(P9_11) + BONE_LED(P9_12) + BONE_LED(P9_13) + BONE_LED(P9_14) + BONE_LED(P9_15) + BONE_LED(P9_16) + BONE_LED(P9_17) + BONE_LED(P9_18) + BONE_LED(P9_19) + BONE_LED(P9_20) + BONE_LED(P9_21) + BONE_LED(P9_22) + BONE_LED(P9_23) + BONE_LED(P9_24) + BONE_LED(P9_25) + BONE_LED(P9_26) + BONE_LED(P9_27) + BONE_LED(P9_28) + BONE_LED(P9_29) + BONE_LED(P9_30) + BONE_LED(P9_31) + + BONE_LED(P9_41) + BONE_LED(P9_91) + BONE_LED(P9_42) + BONE_LED(P9_92) + + BONE_LED(A15) + }; +}; + +// For dummy refrence when peripheral is not available. +&{/} { + not_available: not_available { + // Use ¬_available when required. + // This node is responsible to create these entries, + // /sys/firmware/devicetree/base/__symbols__/not_available + // /sys/firmware/devicetree/base/not_available + }; +}; + +// For compatible bone pinmuxing +bone_pinmux: &am33xx_pinmux { + +}; + +// UARTs +bone_uart_1: &uart1 { + symlink = "bone/uart/1"; +}; + +bone_uart_2: &uart2 { + symlink = "bone/uart/2"; +}; + +bone_uart_3: &uart3 { + symlink = "bone/uart/3"; +}; + +bone_uart_4: &uart4 { + symlink = "bone/uart/4"; +}; + +bone_uart_5: &uart5 { + symlink = "bone/uart/5"; +}; + +// I2Cs +bone_i2c_1: &i2c1 { + symlink = "bone/i2c/1"; +}; + +bone_i2c_2: &i2c2 { + // Already in use for cape EEPROM reading. + symlink = "bone/i2c/2"; +}; + +// use only when bone_i2c_2 is not in use +bone_i2c_2a: &i2c2 { + symlink = "bone/i2c/2a"; +}; + +// use only when bone_i2c_1 is not in use +bone_i2c_3: &i2c1 { + symlink = "bone/i2c/3"; +}; + +// gpmc: +bone_gpmc: &gpmc { + +}; + +// SPIs +bone_spi_0: &spi0 { + +}; + +bone_spi_1: &spi1 { + +}; + +// PWMs +bone_pwm_0: &ehrpwm0 { + +}; + +bone_pwm_1: &ehrpwm1 { + +}; + +bone_pwm_2: &ehrpwm2 { + +}; + +// CAN +bone_can_0: &dcan0 { + +}; + +bone_can_1: &dcan1 { + +}; + +// PWM_TIMERs +bone_timer_0: &timer6 { + +}; + +bone_timer_1: &timer4 { + +}; + +bone_timer_2: &timer7 { + +}; + +bone_timer_3: ¬_available { + // Not available on BBBWl/BBB +}; + +bone_timer_4: &timer5 { + +}; + +bone_timer_5: ¬_available { + // Not available on BBBWL/BBB +}; + +// McASP +bone_mcasp_0: &mcasp0 { + serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */ + 2 0 1 0 + 0 0 0 0 + 0 0 0 0 + 0 0 0 0 + >; +}; + +// eQEP +bone_eqep_0: &eqep0{ + +}; + +bone_eqep_1: &eqep1{ + +}; + +bone_eqep_2: &eqep2{ + +}; + +// ADC +bone_adc: &tscadc { + adc { + ti,adc-channels = <0 1 2 3 4 5 6 7>; + ti,chan-step-avg = <16 16 16 16 16 16 16 16>; + ti,chan-step-opendelay = <0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98>; + ti,chan-step-sampledelay = <0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0>; + }; +}; + +// Enable all pwmss +&epwmss0 { + status = "okay"; +}; + +&epwmss1 { + status = "okay"; +}; + +&epwmss2 { + status = "okay"; +}; + +// spi_gpio (bit banged SPI) +&{/} { + // For CTAG SW 8ch overlay + bone_spi_gpio_ad193x: bone_spi_gpio_ad193x { + status = "disabled"; + compatible = "spi-gpio"; + #address-cells = <1>; + #size-cells = <0>; + + sck-gpios = ; + mosi-gpios = ; + miso-gpios = ; + cs-gpios = ; + num-chipselects = <1>; + + ad193x: ad193x@0{ + compatible = "analog,ad1938"; + reset-gpio = ; + reg = <0>; //corresponds to cs + spi-max-frequency = <100000>; + }; + }; +}; + +// // Bone sound +// &ocp { +// clk_mcasp0_fixed: clk_mcasp0_fixed { +// #clock-cells = <0>; +// compatible = "fixed-clock"; +// clock-frequeny = <24576000>; +// }; +// clk_mcasp0: clk_mcasp0 { +// #clock-cells = <0>; +// compatible = "gpio-gate-clock"; +// clocks = <&clk_mcasp0_fixed>; +// enable-gpios = <&gpio1 27 GPIO_ACTIVE_HIGH>; +// }; +// bone_sound: bone_sound { +// bb-device = <0>; //BBB/BBG +// }; +// }; diff --git a/arch/arm/boot/dts/dra7.dtsi b/arch/arm/boot/dts/dra7.dtsi index f29de22ff85a3..63b8444fe9751 100644 --- a/arch/arm/boot/dts/dra7.dtsi +++ b/arch/arm/boot/dts/dra7.dtsi @@ -884,7 +884,7 @@ }; bb2d: bb2d@59000000 { - compatible = "ti,dra7-bb2d"; + compatible = "ti,dra7-bb2d", "vivante,gc"; reg = <0x59000000 0x0700>; interrupts = ; ti,hwmods = "bb2d"; diff --git a/arch/arm/boot/dts/omap5.dtsi b/arch/arm/boot/dts/omap5.dtsi index 530210db27198..9b6a8b7f20f1a 100644 --- a/arch/arm/boot/dts/omap5.dtsi +++ b/arch/arm/boot/dts/omap5.dtsi @@ -522,6 +522,9 @@ clocks = <&dss_clkctrl OMAP5_DSS_CORE_CLKCTRL 8>, <&dss_clkctrl OMAP5_DSS_CORE_CLKCTRL 10>; clock-names = "fck", "sys_clk"; + + #address-cells = <1>; + #size-cells = <0>; }; }; @@ -554,6 +557,9 @@ clocks = <&dss_clkctrl OMAP5_DSS_CORE_CLKCTRL 8>, <&dss_clkctrl OMAP5_DSS_CORE_CLKCTRL 10>; clock-names = "fck", "sys_clk"; + + #address-cells = <1>; + #size-cells = <0>; }; }; diff --git a/arch/arm/boot/dts/overlays/AM335X-PRU-UIO-00A0.dts b/arch/arm/boot/dts/overlays/AM335X-PRU-UIO-00A0.dts new file mode 100644 index 0000000000000..f3016efca2d7d --- /dev/null +++ b/arch/arm/boot/dts/overlays/AM335X-PRU-UIO-00A0.dts @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + AM335X-PRU-UIO-00A0.kernel = __TIMESTAMP__; + }; +}; + + +&pruss_tm { + status = "okay"; +}; + +&pruss { + compatible = "ti,pruss-v2"; + ti,pintc-offset = <0x20000>; + interrupt-parent = <&intc>; + interrupts = <20 21 22 23 24 25 26 27>; +}; diff --git a/arch/arm/boot/dts/overlays/AM57XX-PRU-UIO-00A0.dts b/arch/arm/boot/dts/overlays/AM57XX-PRU-UIO-00A0.dts new file mode 100644 index 0000000000000..b3f2aaf9abc09 --- /dev/null +++ b/arch/arm/boot/dts/overlays/AM57XX-PRU-UIO-00A0.dts @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + AM57XX-PRU-UIO-00A0.kernel = __TIMESTAMP__; + }; +}; + +&pruss1_tm { + status = "okay"; +}; + +&pruss1 { + compatible = "ti,pruss-v2"; + ti,pintc-offset = <0x20000>; + interrupts = , + , + , + , + , + , + , + ; + pruss-instance = "pruss1"; +}; + +&pruss2_tm { + status = "okay"; +}; + +&pruss2 { + compatible = "ti,pruss-v2"; + ti,pintc-offset = <0x20000>; + interrupts = , + , + , + , + , + , + , + ; + pruss-instance = "pruss2"; +}; diff --git a/arch/arm/boot/dts/overlays/BB-ADC-00A0.dts b/arch/arm/boot/dts/overlays/BB-ADC-00A0.dts new file mode 100644 index 0000000000000..914b7aa3df433 --- /dev/null +++ b/arch/arm/boot/dts/overlays/BB-ADC-00A0.dts @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-ADC-00A0.kernel = __TIMESTAMP__; + }; +}; + +&tscadc { + status = "okay"; + adc { + // Configure one or more (up to 8) steps for the adc to execute: + + + // For each step, the channel to sample. + // range: 0 .. 7 + ti,adc-channels = <0 1 2 3 4 5 6 7>; + // + // BeagleBone Black (and most other variants): + // ch 0 P9.39 + // ch 1 P9.40 + // ch 2 P9.37 + // ch 3 P9.38 + // ch 4 P9.33 + // ch 5 P9.36 + // ch 6 P9.35 + // ch 7 measures 0.5 * VDD_3V3B with 2.4 kΩ source impedance + // + // PocketBeagle: + // ch 0 P1.19 + // ch 1 P1.21 + // ch 2 P1.23 + // ch 3 P1.25 + // ch 4 P1.27 + // ch 5 P2.35 via 10k/10k voltage divider + // ch 6 P1.02 via 10k/10k voltage divider + // ch 7 P2.36 via pmic mux + // + // The divider used on PocketBeagle channels 5 and 6 makes the effective voltage V_eff and + // source impedance Z_eff seen by the adc on these channels depend on the voltage V_src and + // impedance Z_src of the source connected to the corresponding pin as follows: + // + // V_eff = V_src / (2 + Z_src / (10 kΩ)) + // Z_eff = 5 kΩ * (1 + Z_src / (Z_src + 20 kΩ)) + // ≈ 5 kΩ + Z_src / 4 for small values of Z_src (up to 2 kΩ or so) + + + // For each step, number of adc clock cycles to wait between setting up muxes and sampling. + // range: 0 .. 262143 + // optional, default is 152 (XXX but why?!) + ti,chan-step-opendelay = <152 152 152 152 152 152 152 152>; + //` + // XXX is there any purpose to set this nonzero other than to fine-tune the sample rate? + + + // For each step, how many times it should sample to average. + // range: 1 .. 16, must be power of two (i.e. 1, 2, 4, 8, or 16) + // optional, default is 16 + ti,chan-step-avg = <16 16 16 16 16 16 16 16>; + // + // If you're using periodic sampling (using the iio block device rather than sysfs) then + // you should consider setting this to 1 and if desired reduce the samplerate in userspace + // instead since averaging isn't a particularly good low-pass filter. + // + // If you're using sysfs to occasionally read a value, then the default value of 16 will + // still get you the most accurate readings. + + + // For each step, number of adc clock cycles to sample minus two. + // range: 0 .. 255 (resulting in sampling time of 2 .. 257 cycles) + // optional, default is 0 + ti,chan-step-sampledelay = <0 0 0 0 0 0 0 0>; + // + // If this is set too low, accuracy will deteriorate when the thing you're measuring has a + // high source impedance. The maximum source impedance recommended (by erratum 1.0.32) is: + // (2 + sampledelay) * 2.873 kΩ - 0.2 kΩ + // which means that the default should be fine for source impedance up to 5.5 kΩ. + // + // (This seems to ensure the sampling time is at least 21 times the RC constant, based on + // the 5.5 pF nominal capacitance specified in the datasheet.) + + + // After sampling, conversion time is 13 adc clock cycles. + // + // The adc clock frequency is 3 MHz, therefore the total time per step in microseconds is: + // ( opendelay + avg * ( 2 + sampledelay + 13 ) ) / 3 + // + // If all steps use the same timings then the sample rate will be: + // 3 MHz / ( opendelay + avg * ( 2 + sampledelay + 13 ) ) / number_of_steps + // + // The highest samplerate obtainable (avg=1, opendelay=0, sampledelay=0) is therefore: + // 200 kHz / number_of_steps + // = 25 kHz when using all 8 steps. + // + // Using avg=16 reduces that to: + // 12.5 kHz / number_of_steps + // = 1.5625 kHz when using all 8 steps. + // + // Using the default values (avg=16, opendelay=152, sampledelay=0) reduces that to: + // 7.653 kHz / number_of_steps + // = 0.9566 kHz when using all 8 steps. + }; +}; diff --git a/arch/arm/boot/dts/overlays/BB-BBBW-WL1835-00A0.dts b/arch/arm/boot/dts/overlays/BB-BBBW-WL1835-00A0.dts new file mode 100644 index 0000000000000..1f09a3999bfa1 --- /dev/null +++ b/arch/arm/boot/dts/overlays/BB-BBBW-WL1835-00A0.dts @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-BBBW-WL1835-00A0.kernel = __TIMESTAMP__; + }; +}; + +&{/} { + model = "TI AM335x BeagleBone Black Wireless"; + compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + wlan_en_reg: fixedregulator@2 { + compatible = "regulator-fixed"; + regulator-name = "wlan-en-regulator"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + startup-delay-us= <70000>; + + /* WL_EN */ + gpio = <&gpio3 9 0>; + enable-active-high; + }; +}; + +&am33xx_pinmux { + bt_pins: pinmux_bt_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_TXD0, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gmii1_txd0.gpio0_28 - BT_EN */ + >; + }; + + mmc3_pins: pinmux_mmc3_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_RXD1, PIN_INPUT_PULLUP, MUX_MODE6 ) /* (L15) gmii1_rxd1.mmc2_clk */ + AM33XX_PADCONF(AM335X_PIN_MII1_TX_EN, PIN_INPUT_PULLUP, MUX_MODE6 ) /* (J16) gmii1_txen.mmc2_cmd */ + AM33XX_PADCONF(AM335X_PIN_MII1_RX_DV, PIN_INPUT_PULLUP, MUX_MODE5 ) /* (J17) gmii1_rxdv.mmc2_dat0 */ + AM33XX_PADCONF(AM335X_PIN_MII1_TXD3, PIN_INPUT_PULLUP, MUX_MODE5 ) /* (J18) gmii1_txd3.mmc2_dat1 */ + AM33XX_PADCONF(AM335X_PIN_MII1_TXD2, PIN_INPUT_PULLUP, MUX_MODE5 ) /* (K15) gmii1_txd2.mmc2_dat2 */ + AM33XX_PADCONF(AM335X_PIN_MII1_COL, PIN_INPUT_PULLUP, MUX_MODE5 ) /* (H16) gmii1_col.mmc2_dat3 */ + >; + }; + + uart3_pins: pinmux_uart3_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_RXD3, PIN_INPUT_PULLUP, MUX_MODE1) /* gmii1_rxd3.uart3_rxd */ + AM33XX_PADCONF(AM335X_PIN_MII1_RXD2, PIN_OUTPUT_PULLDOWN, MUX_MODE1) /* gmii1_rxd2.uart3_txd */ + AM33XX_PADCONF(AM335X_PIN_MDIO, PIN_INPUT, MUX_MODE3) /* mdio_data.uart3_ctsn */ + AM33XX_PADCONF(AM335X_PIN_MDC, PIN_OUTPUT_PULLDOWN, MUX_MODE3) /* mdio_clk.uart3_rtsn */ + >; + }; + + wl18xx_pins: pinmux_wl18xx_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_TX_CLK, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gmii1_txclk.gpio3_9 WL_EN */ + AM33XX_PADCONF(AM335X_PIN_RMII1_REF_CLK, PIN_INPUT_PULLDOWN, MUX_MODE7) /* rmii1_refclk.gpio0_29 WL_IRQ */ + AM33XX_PADCONF(AM335X_PIN_MII1_RX_CLK, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gmii1_rxclk.gpio3_10 LS_BUF_EN */ + >; + }; +}; + +&mac { + status = "disabled"; +}; + +&mmc3 { + dmas = <&edma_xbar 12 0 1 + &edma_xbar 13 0 2>; + dma-names = "tx", "rx"; + status = "okay"; + vmmc-supply = <&wlan_en_reg>; + bus-width = <4>; + non-removable; + cap-power-off-card; + keep-power-in-suspend; + pinctrl-names = "default"; + pinctrl-0 = <&mmc3_pins &wl18xx_pins>; + + #address-cells = <1>; + #size-cells = <0>; + wlcore: wlcore@2 { + compatible = "ti,wl1835"; + reg = <2>; + interrupt-parent = <&gpio0>; + interrupts = <29 IRQ_TYPE_EDGE_RISING>; + }; +}; + +&uart3 { + pinctrl-names = "default"; + pinctrl-0 = <&uart3_pins &bt_pins>; + status = "okay"; + + bluetooth { + compatible = "ti,wl1835-st"; + enable-gpios = <&gpio0 28 GPIO_ACTIVE_HIGH>; + }; +}; + +&gpio3 { + ls-buf-en-hog { + gpio-hog; + gpios = <10 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "LS_BUF_EN"; + }; +}; diff --git a/arch/arm/boot/dts/overlays/BB-BBGG-WL1835-00A0.dts b/arch/arm/boot/dts/overlays/BB-BBGG-WL1835-00A0.dts new file mode 100644 index 0000000000000..1db7144176c71 --- /dev/null +++ b/arch/arm/boot/dts/overlays/BB-BBGG-WL1835-00A0.dts @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-BBGG-WL1835-00A0.kernel = __TIMESTAMP__; + wl1835_bt = "S3-texas-300000"; + }; +}; + +&{/} { + model = "SeeedStudio BeagleBone Green Gateway"; + compatible = "ti,am335x-bone-green-gateway", "ti,am335x-bone-green", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + aliases { + rtc0 = &extrtc; + rtc1 = "/ocp/rtc@44e3e000"; + }; + + wlan_en_reg: fixedregulator@2 { + compatible = "regulator-fixed"; + regulator-name = "wlan-en-regulator"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + startup-delay-us= <70000>; + + /* WL_EN */ + gpio = <&gpio3 9 0>; + enable-active-high; + }; + + leds { + pinctrl-names = "default"; + //pinctrl-0 = <&user_leds_s0>; + pinctrl-0 = <&user_leds_s0 &bt_pins>; + + compatible = "gpio-leds"; + + led2 { + label = "beaglebone:green:usr0"; + gpios = <&gpio1 21 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + default-state = "off"; + }; + + led3 { + label = "beaglebone:green:usr1"; + gpios = <&gpio1 22 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "mmc0"; + default-state = "off"; + }; + + led4 { + label = "beaglebone:green:usr2"; + gpios = <&gpio1 23 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "cpu0"; + default-state = "off"; + }; + + led5 { + label = "beaglebone:green:usr3"; + gpios = <&gpio1 24 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "mmc1"; + default-state = "off"; + }; + + led6 { + label = "beaglebone:green:usr4"; + gpios = <&gpio2 21 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "netdev"; + default-state = "off"; + }; + + wl18xx_bt_en: led7 { + label = "wl18xx_bt_en"; + gpios = <&gpio0 28 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + }; +}; + +&am33xx_pinmux { + pinctrl-names = "default"; + pinctrl-0 = <&usbhost_pins>; + + user_leds_s0: user_leds_s0 { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_A5, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gpmc_a5.gpio1_21 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_A6, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gpmc_a6.gpio1_22 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_A7, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gpmc_a7.gpio1_23 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_A8, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gpmc_a8.gpio1_24 */ + AM33XX_PADCONF(AM335X_PIN_MII1_RXD0, PIN_OUTPUT_PULLUP, MUX_MODE7) /* WL_Active_LED / USR4 */ + >; + }; + + bt_pins: pinmux_bt_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_TXD0, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gmii1_txd0.gpio0_28 - BT_EN */ + >; + }; + + mmc3_pins: pinmux_mmc3_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_RXD1, PIN_INPUT_PULLUP, MUX_MODE6 ) /* (L15) gmii1_rxd1.mmc2_clk */ + AM33XX_PADCONF(AM335X_PIN_MII1_TX_EN, PIN_INPUT_PULLUP, MUX_MODE6 ) /* (J16) gmii1_txen.mmc2_cmd */ + AM33XX_PADCONF(AM335X_PIN_MII1_RX_DV, PIN_INPUT_PULLUP, MUX_MODE5 ) /* (J17) gmii1_rxdv.mmc2_dat0 */ + AM33XX_PADCONF(AM335X_PIN_MII1_TXD3, PIN_INPUT_PULLUP, MUX_MODE5 ) /* (J18) gmii1_txd3.mmc2_dat1 */ + AM33XX_PADCONF(AM335X_PIN_MII1_TXD2, PIN_INPUT_PULLUP, MUX_MODE5 ) /* (K15) gmii1_txd2.mmc2_dat2 */ + AM33XX_PADCONF(AM335X_PIN_MII1_COL, PIN_INPUT_PULLUP, MUX_MODE5 ) /* (H16) gmii1_col.mmc2_dat3 */ + >; + }; + + uart2_grove_pins: pinmux_uart2_grove_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x90c, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE6) + AM33XX_IOPAD(0x910, PIN_OUTPUT_PULLUP | INPUT_EN | MUX_MODE6) + >; + }; + + uart3_pins: pinmux_uart3_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_RXD3, PIN_INPUT_PULLUP, MUX_MODE1) /* gmii1_rxd3.uart3_rxd */ + AM33XX_PADCONF(AM335X_PIN_MII1_RXD2, PIN_OUTPUT_PULLDOWN, MUX_MODE1) /* gmii1_rxd2.uart3_txd */ + AM33XX_PADCONF(AM335X_PIN_MDIO, PIN_INPUT, MUX_MODE3) /* mdio_data.uart3_ctsn */ + AM33XX_PADCONF(AM335X_PIN_MDC, PIN_OUTPUT_PULLDOWN, MUX_MODE3) /* mdio_clk.uart3_rtsn */ + >; + }; + + usbhost_pins: pinmux_usbhost_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_TXD1, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gmii1_txd1.gpio0[21] */ + >; + }; + + wl18xx_pins: pinmux_wl18xx_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_TX_CLK, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gmii1_txclk.gpio3_9 WL_EN */ + AM33XX_PADCONF(AM335X_PIN_RMII1_REF_CLK, PIN_INPUT_PULLDOWN, MUX_MODE7) /* rmii1_refclk.gpio0_29 WL_IRQ */ + AM33XX_PADCONF(AM335X_PIN_MII1_RX_CLK, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gmii1_rxclk.gpio3_10 LS_BUF_EN */ + >; + }; +}; + +&mac { + status = "disabled"; +}; + +&mmc3 { + dmas = <&edma_xbar 12 0 1 + &edma_xbar 13 0 2>; + dma-names = "tx", "rx"; + status = "okay"; + vmmc-supply = <&wlan_en_reg>; + bus-width = <4>; + non-removable; + cap-power-off-card; + keep-power-in-suspend; + pinctrl-names = "default"; + pinctrl-0 = <&mmc3_pins &wl18xx_pins>; + + #address-cells = <1>; + #size-cells = <0>; + wlcore: wlcore@2 { + compatible = "ti,wl1835"; + reg = <2>; + interrupt-parent = <&gpio0>; + interrupts = <29 IRQ_TYPE_EDGE_RISING>; + }; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_grove_pins>; + status = "okay"; +}; + +&uart3 { + pinctrl-names = "default"; + pinctrl-0 = <&uart3_pins>; + //pinctrl-0 = <&uart3_pins &bt_pins>; + status = "okay"; + + //bluetooth { + // compatible = "ti,wl1835-st"; + // enable-gpios = <&gpio0 28 GPIO_ACTIVE_HIGH>; + //}; +}; + +&i2c0 { + #address-cells = <1>; + #size-cells = <0>; + + extrtc: rtc@68 { + compatible = "dallas,ds1340"; + reg = <0x68>; + }; +}; + +// (K16) gmii1_txd1.gpio0[21] +&gpio0 { + usb-reset-hog { + gpio-hog; + gpios = <21 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "usb_reset"; + }; +}; + +&gpio3 { + ls-buf-en-hog { + gpio-hog; + gpios = <10 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "LS_BUF_EN"; + }; +}; + +&usb1 { + #address-cells = <1>; + #size-cells = <0>; + + hub@1 { + compatible = "usb424,9512"; + reg = <1>; + + #address-cells = <1>; + #size-cells = <0>; + + ethernet: ethernet@1 { + compatible = "usb424,ec00"; + reg = <1>; + }; + }; +}; diff --git a/arch/arm/boot/dts/overlays/BB-BBGW-WL1835-00A0.dts b/arch/arm/boot/dts/overlays/BB-BBGW-WL1835-00A0.dts new file mode 100644 index 0000000000000..d1a6702e1e15c --- /dev/null +++ b/arch/arm/boot/dts/overlays/BB-BBGW-WL1835-00A0.dts @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-BBGW-WL1835-00A0.kernel = __TIMESTAMP__; + wl1835_bt = "S3-texas-300000"; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P9_12_pinmux { status = "disabled"; }; /* gpmc_ad12.gpio1_28 BT_EN */ + P8_12_pinmux { status = "disabled"; }; /* gpmc_ad12.mmc2_dat0 */ + P8_11_pinmux { status = "disabled"; }; /* gpmc_ad13.mmc2_dat1 */ + P8_16_pinmux { status = "disabled"; }; /* gpmc_ad14.mmc2_dat2 */ + P8_15_pinmux { status = "disabled"; }; /* gpmc_ad15.mmc2_dat3 */ + + P8_18_pinmux { status = "disabled"; }; /* gpmc_clk.mmc2_clk */ + + //Audio... + P9_28_pinmux { status = "disabled"; }; + P9_29_pinmux { status = "disabled"; }; + P9_31_pinmux { status = "disabled"; }; + + /* wl1835 */ + P8_14_pinmux { status = "disabled"; }; /* wl1835: wl_en */ + P8_17_pinmux { status = "disabled"; }; /* wl1835: wl_irq */ + P8_26_pinmux { status = "disabled"; }; /* wl1835: LS_BUF_EN */ + P9_30_pinmux { status = "disabled"; }; /* wl1835: MCASP0_AHCLKR */ +}; + +&{/} { + model = "TI AM335x BeagleBone Green Wireless"; + compatible = "ti,am335x-bone-green-wireless", "ti,am335x-bone-green", "ti,am335x-bone", "ti,am33xx"; + + wlan_en_reg: fixedregulator@2 { + compatible = "regulator-fixed"; + regulator-name = "wlan-en-regulator"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + startup-delay-us= <70000>; + + /* WL_EN */ + gpio = <&gpio0 26 0>; + enable-active-high; + }; + + leds { + pinctrl-names = "default"; + //pinctrl-0 = <&user_leds_s0>; + pinctrl-0 = <&user_leds_s0 &bt_pins>; + + compatible = "gpio-leds"; + + led2 { + label = "beaglebone:green:usr0"; + gpios = <&gpio1 21 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + default-state = "off"; + }; + + led3 { + label = "beaglebone:green:usr1"; + gpios = <&gpio1 22 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "mmc0"; + default-state = "off"; + }; + + led4 { + label = "beaglebone:green:usr2"; + gpios = <&gpio1 23 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "cpu0"; + default-state = "off"; + }; + + led5 { + label = "beaglebone:green:usr3"; + gpios = <&gpio1 24 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "mmc1"; + default-state = "off"; + }; + + wl18xx_bt_en: led7 { + label = "wl18xx_bt_en"; + gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + }; +}; + +&am33xx_pinmux { + user_leds_s0: user_leds_s0 { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_A5, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gpmc_a5.gpio1_21 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_A6, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gpmc_a6.gpio1_22 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_A7, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gpmc_a7.gpio1_23 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_A8, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gpmc_a8.gpio1_24 */ + >; + }; + + bt_pins: pinmux_bt_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_BEN1, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gpmc_ad12.gpio1_28 BT_EN */ + >; + }; + + mmc3_pins: pinmux_mmc3_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_AD12, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad12.mmc2_dat0 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD13, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad13.mmc2_dat1 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD14, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad14.mmc2_dat2 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD15, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_ad15.mmc2_dat3 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_CSN3, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_csn3.mmc2_cmd */ + AM33XX_PADCONF(AM335X_PIN_GPMC_CLK, PIN_INPUT_PULLUP, MUX_MODE3) /* gpmc_clk.mmc2_clk */ + >; + }; + + uart3_pins: pinmux_uart3_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MII1_RXD3, PIN_INPUT_PULLUP, MUX_MODE1) /* gmii1_rxd3.uart3_rxd */ + AM33XX_PADCONF(AM335X_PIN_MII1_RXD2, PIN_OUTPUT_PULLDOWN, MUX_MODE1) /* gmii1_rxd2.uart3_txd */ + AM33XX_PADCONF(AM335X_PIN_MDIO, PIN_INPUT, MUX_MODE3) /* mdio_data.uart3_ctsn */ + AM33XX_PADCONF(AM335X_PIN_MDC, PIN_OUTPUT_PULLDOWN, MUX_MODE3) /* mdio_clk.uart3_rtsn */ + >; + }; + + wl18xx_pins: pinmux_wl18xx_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_AD10, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gpmc_ad10.gpio0_26 WL_EN */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD11, PIN_INPUT_PULLDOWN, MUX_MODE7) /* gpmc_ad11.gpio0_27 WL_IRQ */ + AM33XX_PADCONF(AM335X_PIN_GPMC_CSN0, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gpmc_csn0.gpio1_29 LS_BUF_EN */ + >; + }; +}; + +&mac { + status = "disabled"; +}; + +&mmc3 { + dmas = <&edma_xbar 12 0 1 + &edma_xbar 13 0 2>; + dma-names = "tx", "rx"; + status = "okay"; + vmmc-supply = <&wlan_en_reg>; + bus-width = <4>; + non-removable; + cap-power-off-card; + keep-power-in-suspend; + pinctrl-names = "default"; + pinctrl-0 = <&mmc3_pins &wl18xx_pins>; + + #address-cells = <1>; + #size-cells = <0>; + wlcore: wlcore@2 { + compatible = "ti,wl1835"; + reg = <2>; + interrupt-parent = <&gpio0>; + interrupts = <27 IRQ_TYPE_EDGE_RISING>; + }; +}; + +&uart3 { + pinctrl-names = "default"; + pinctrl-0 = <&uart3_pins>; + //pinctrl-0 = <&uart3_pins &bt_pins>; + status = "okay"; + + //bluetooth { + // compatible = "ti,wl1835-st"; + // enable-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>; + //}; +}; + +&gpio1 { + ls-buf-en-hog { + gpio-hog; + gpios = <29 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "LS_BUF_EN"; + }; +}; + +/* BT_AUD_OUT from wl1835 has to be pulled low when WL_EN is activated.*/ +/* in case it isn't, wilink8 ends up in one of the test modes that */ +/* intruces various issues (elp wkaeup timeouts etc.) */ +/* On the BBGW this pin is routed through the level shifter (U21) that */ +/* introduces a pullup on the line and wilink8 ends up in a bad state. */ +/* use a gpio hog to force this pin low. An alternative may be adding */ +/* an external pulldown on U21 pin 4. */ + +&gpio3 { + bt-aud-in-hog { + gpio-hog; + gpios = <16 GPIO_ACTIVE_HIGH>; + output-low; + line-name = "MCASP0_AHCLKR"; + }; +}; diff --git a/arch/arm/boot/dts/overlays/BB-BONE-4D4C-01-00A1.dts b/arch/arm/boot/dts/overlays/BB-BONE-4D4C-01-00A1.dts new file mode 100644 index 0000000000000..48cd7d7b18164 --- /dev/null +++ b/arch/arm/boot/dts/overlays/BB-BONE-4D4C-01-00A1.dts @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-BONE-4D4C-01-00A1.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P8_45_pinmux { status = "disabled"; }; /* lcd: lcd_data0 */ + P8_46_pinmux { status = "disabled"; }; /* lcd: lcd_data1 */ + P8_43_pinmux { status = "disabled"; }; /* lcd: lcd_data2 */ + P8_44_pinmux { status = "disabled"; }; /* lcd: lcd_data3 */ + P8_41_pinmux { status = "disabled"; }; /* lcd: lcd_data4 */ + P8_42_pinmux { status = "disabled"; }; /* lcd: lcd_data5 */ + P8_39_pinmux { status = "disabled"; }; /* lcd: lcd_data6 */ + P8_40_pinmux { status = "disabled"; }; /* lcd: lcd_data7 */ + P8_37_pinmux { status = "disabled"; }; /* lcd: lcd_data8 */ + P8_38_pinmux { status = "disabled"; }; /* lcd: lcd_data9 */ + P8_36_pinmux { status = "disabled"; }; /* lcd: lcd_data10 */ + P8_34_pinmux { status = "disabled"; }; /* lcd: lcd_data11 */ + P8_35_pinmux { status = "disabled"; }; /* lcd: lcd_data12 */ + P8_33_pinmux { status = "disabled"; }; /* lcd: lcd_data13 */ + P8_31_pinmux { status = "disabled"; }; /* lcd: lcd_data14 */ + P8_32_pinmux { status = "disabled"; }; /* lcd: lcd_data15 */ + + P8_27_pinmux { status = "disabled"; }; /* lcd: lcd_vsync */ + P8_29_pinmux { status = "disabled"; }; /* lcd: lcd_hsync */ + P8_28_pinmux { status = "disabled"; }; /* lcd: lcd_pclk */ + P8_30_pinmux { status = "disabled"; }; /* lcd: lcd_ac_bias_en */ + + P9_27_pinmux { status = "disabled"; }; /* lcd: gpio3_19 DISPEN */ + + P9_14_pinmux { status = "disabled"; }; /* pwm: ehrpwm1a PWM_BL */ + + P9_18_pinmux { status = "disabled"; }; /* i2c1_sda */ + P9_17_pinmux { status = "disabled"; }; /* i2c1_scl */ + P9_26_pinmux { status = "disabled"; }; /* touch interrupt on gpio0_14 */ +}; + +&am33xx_pinmux { + bb_lcd_pwm_backlight_pins: pinmux_bb_lcd_pwm_backlight_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_A2, PIN_OUTPUT, MUX_MODE6) /* gpmc_a2.ehrpwm1a, OMAP_MUX_MODE6 | AM33XX_PIN_OUTPUT */ + >; + }; + + bb_lcd_lcd_pins: pinmux_bb_lcd_lcd_pins { + pinctrl-single,pins = < + /*LCD enable */ + AM33XX_PADCONF(AM335X_PIN_MCASP0_FSR, PIN_OUTPUT, MUX_MODE7) /* mcasp0_fsr.gpio3_19, OUTPUT | MODE7 LCD DISEN */ + + AM33XX_PADCONF(AM335X_PIN_LCD_DATA0, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA1, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA2, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA3, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA4, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA5, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA6, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA7, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA8, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA9, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA10, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA11, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA12, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA13, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA14, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA15, PIN_OUTPUT, MUX_MODE0) + + AM33XX_PADCONF(AM335X_PIN_LCD_VSYNC, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_HSYNC, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_PCLK, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_AC_BIAS_EN, PIN_OUTPUT, MUX_MODE0) + >; + }; + + bb_i2c1_pins: pinmux_bb_i2c1_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x958, PIN_INPUT_PULLUP | SLEWCTRL_SLOW | MUX_MODE2) /* spi0_d1.i2c1_sda */ + AM33XX_IOPAD(0x95C, PIN_INPUT_PULLUP | SLEWCTRL_SLOW | MUX_MODE2) /* spi0_cs0.i2c1_scl */ + >; + }; + + edt_ft5x06_pins: pinmux_edt_ft5x06_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_UART1_RXD, PIN_INPUT, MUX_MODE7) + >; + }; +}; + +&epwmss1 { + status = "okay"; +}; + +&ehrpwm1 { + pinctrl-names = "default"; + pinctrl-0 = <&bb_lcd_pwm_backlight_pins>; + status = "okay"; +}; + +&lcdc { + status = "okay"; + + blue-and-red-wiring = "straight"; + + //FIXME - LCD doesn't init... + //port { + // lcdc_0: endpoint@0 { + // remote-endpoint = <&panel_0>; + // }; + //}; +}; + +&i2c1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_i2c1_pins>; + + clock-frequency = <100000>; + + #address-cells = <1>; + #size-cells = <0>; + + polytouch: edt-ft5x06@38 { + status = "okay"; + compatible = "edt,edt-ft5x06"; + reg = <0x38>; + pinctrl-names = "default"; + pinctrl-0 = <&edt_ft5x06_pins>; + interrupt-parent = <&gpio0>; + interrupts = <14 2>; + + touchscreen-size-y = <480>; + touchscreen-size-x = <272>; + + touchscreen-swapped-x-y; + }; +}; + +&{/} { + backlight: backlight { + status = "okay"; + compatible = "pwm-backlight"; + pwms = <&ehrpwm1 0 500000 0>; + brightness-levels = < + 0 1 2 3 4 5 6 7 8 9 + 10 11 12 13 14 15 16 17 18 19 + 20 21 22 23 24 25 26 27 28 29 + 30 31 32 33 34 35 36 37 38 39 + 40 41 42 43 44 45 46 47 48 49 + 50 51 52 53 54 55 56 57 58 59 + 60 61 62 63 64 65 66 67 68 69 + 70 71 72 73 74 75 76 77 78 79 + 80 81 82 83 84 85 86 87 88 89 + 90 91 92 93 94 95 96 97 98 99 + 100 + >; + default-brightness-level = <100>; + }; + + panel { + status = "okay"; + compatible = "ti,tilcdc,panel"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_lcd_lcd_pins>; + backlight = <&backlight>; + enable-gpios = <&gpio3 19 0>; + + //FIXME - LCD doesn't init... + //port { + // panel_0: endpoint@0 { + // remote-endpoint = <&lcdc_0>; + // }; + //}; + + panel-info { + ac-bias = <255>; + ac-bias-intrpt = <0>; + dma-burst-sz = <16>; + bpp = <16>; + fdd = <0x80>; + sync-edge = <0>; + sync-ctrl = <1>; + raster-order = <0>; + fifo-th = <0>; + }; + + display-timings { + native-mode = <&timing0>; + /* www.newhavendisplay.com/app_notes/OTA5180A.pdf */ + timing0: 480x272 { + clock-frequency = <9200000>; + hactive = <480>; + vactive = <272>; + hfront-porch = <8>; + hback-porch = <47>; + hsync-len = <41>; + vback-porch = <2>; + vfront-porch = <3>; + vsync-len = <10>; + hsync-active = <0>; + vsync-active = <0>; + de-active = <1>; + pixelclk-active = <0>; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/overlays/BB-BONE-4D5R-01-00A1.dts b/arch/arm/boot/dts/overlays/BB-BONE-4D5R-01-00A1.dts new file mode 100644 index 0000000000000..2c3a82b7a6297 --- /dev/null +++ b/arch/arm/boot/dts/overlays/BB-BONE-4D5R-01-00A1.dts @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-BONE-4D5R-01-00A1.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P8_45_pinmux { status = "disabled"; }; /* lcd: lcd_data0 */ + P8_46_pinmux { status = "disabled"; }; /* lcd: lcd_data1 */ + P8_43_pinmux { status = "disabled"; }; /* lcd: lcd_data2 */ + P8_44_pinmux { status = "disabled"; }; /* lcd: lcd_data3 */ + P8_41_pinmux { status = "disabled"; }; /* lcd: lcd_data4 */ + P8_42_pinmux { status = "disabled"; }; /* lcd: lcd_data5 */ + P8_39_pinmux { status = "disabled"; }; /* lcd: lcd_data6 */ + P8_40_pinmux { status = "disabled"; }; /* lcd: lcd_data7 */ + P8_37_pinmux { status = "disabled"; }; /* lcd: lcd_data8 */ + P8_38_pinmux { status = "disabled"; }; /* lcd: lcd_data9 */ + P8_36_pinmux { status = "disabled"; }; /* lcd: lcd_data10 */ + P8_34_pinmux { status = "disabled"; }; /* lcd: lcd_data11 */ + P8_35_pinmux { status = "disabled"; }; /* lcd: lcd_data12 */ + P8_33_pinmux { status = "disabled"; }; /* lcd: lcd_data13 */ + P8_31_pinmux { status = "disabled"; }; /* lcd: lcd_data14 */ + P8_32_pinmux { status = "disabled"; }; /* lcd: lcd_data15 */ + + P8_27_pinmux { status = "disabled"; }; /* lcd: lcd_vsync */ + P8_29_pinmux { status = "disabled"; }; /* lcd: lcd_hsync */ + P8_28_pinmux { status = "disabled"; }; /* lcd: lcd_pclk */ + P8_30_pinmux { status = "disabled"; }; /* lcd: lcd_ac_bias_en */ + + P9_27_pinmux { status = "disabled"; }; /* lcd: gpio3_19 DISPEN */ + + P9_14_pinmux { status = "disabled"; }; /* pwm: ehrpwm1a PWM_BL */ + + P9_18_pinmux { status = "disabled"; }; /* i2c1_sda */ + P9_17_pinmux { status = "disabled"; }; /* i2c1_scl */ + P9_26_pinmux { status = "disabled"; }; /* touch interrupt on gpio0_14 */ +}; + +&am33xx_pinmux { + bb_lcd_pwm_backlight_pins: pinmux_bb_lcd_pwm_backlight_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_A2, PIN_OUTPUT, MUX_MODE6) /* gpmc_a2.ehrpwm1a, OMAP_MUX_MODE6 | AM33XX_PIN_OUTPUT */ + >; + }; + + bb_lcd_lcd_pins: pinmux_bb_lcd_lcd_pins { + pinctrl-single,pins = < + /*LCD enable */ + AM33XX_PADCONF(AM335X_PIN_MCASP0_FSR, PIN_OUTPUT, MUX_MODE7) /* mcasp0_fsr.gpio3_19, OUTPUT | MODE7 LCD DISEN */ + + AM33XX_PADCONF(AM335X_PIN_LCD_DATA0, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA1, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA2, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA3, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA4, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA5, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA6, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA7, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA8, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA9, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA10, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA11, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA12, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA13, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA14, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA15, PIN_OUTPUT, MUX_MODE0) + + AM33XX_PADCONF(AM335X_PIN_LCD_VSYNC, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_HSYNC, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_PCLK, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_AC_BIAS_EN, PIN_OUTPUT, MUX_MODE0) + >; + }; + + bb_i2c1_pins: pinmux_bb_i2c1_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x958, PIN_INPUT_PULLUP | SLEWCTRL_SLOW | MUX_MODE2) /* spi0_d1.i2c1_sda */ + AM33XX_IOPAD(0x95C, PIN_INPUT_PULLUP | SLEWCTRL_SLOW | MUX_MODE2) /* spi0_cs0.i2c1_scl */ + >; + }; + + ar1021_pins: pinmux_ar1021_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_UART1_RXD, PIN_INPUT, MUX_MODE7) + >; + }; +}; + +&epwmss1 { + status = "okay"; +}; + +&ehrpwm1 { + pinctrl-names = "default"; + pinctrl-0 = <&bb_lcd_pwm_backlight_pins>; + status = "okay"; +}; + +&lcdc { + status = "okay"; + + blue-and-red-wiring = "straight"; + + //FIXME - LCD doesn't init... + //port { + // lcdc_0: endpoint@0 { + // remote-endpoint = <&panel_0>; + // }; + //}; +}; + +&i2c1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_i2c1_pins>; + + clock-frequency = <100000>; + + #address-cells = <1>; + #size-cells = <0>; + + ar1021: ar1021@4d { + status = "okay"; + compatible = "microchip,ar1021-i2c"; + reg = <0x4d>; + pinctrl-names = "default"; + pinctrl-0 = <&ar1021_pins>; + interrupt-parent = <&gpio0>; + interrupts = <14 IRQ_TYPE_LEVEL_HIGH>; + + touchscreen-offset-x=<250>; + touchscreen-offset-y=<300>; + + touchscreen-inverted-y; + }; +}; + +&{/} { + backlight: backlight { + status = "okay"; + compatible = "pwm-backlight"; + pwms = <&ehrpwm1 0 500000 0>; + brightness-levels = < + 0 1 2 3 4 5 6 7 8 9 + 10 11 12 13 14 15 16 17 18 19 + 20 21 22 23 24 25 26 27 28 29 + 30 31 32 33 34 35 36 37 38 39 + 40 41 42 43 44 45 46 47 48 49 + 50 51 52 53 54 55 56 57 58 59 + 60 61 62 63 64 65 66 67 68 69 + 70 71 72 73 74 75 76 77 78 79 + 80 81 82 83 84 85 86 87 88 89 + 90 91 92 93 94 95 96 97 98 99 + 100 + >; + default-brightness-level = <100>; + }; + + panel { + status = "okay"; + compatible = "ti,tilcdc,panel"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_lcd_lcd_pins>; + backlight = <&backlight>; + enable-gpios = <&gpio3 19 0>; + + //FIXME - LCD doesn't init... + //port { + // panel_0: endpoint@0 { + // remote-endpoint = <&lcdc_0>; + // }; + //}; + + panel-info { + ac-bias = <255>; + ac-bias-intrpt = <0>; + dma-burst-sz = <16>; + bpp = <16>; + fdd = <0x80>; + sync-edge = <0>; + sync-ctrl = <1>; + raster-order = <0>; + fifo-th = <0>; + }; + + display-timings { + native-mode = <&timing0>; + /* Settings for ThreeFive S9700RTWV35TR / LCD7 cape: */ + timing0: 800x480 { + clock-frequency = <30000000>; + hactive = <800>; + vactive = <480>; + hfront-porch = <40>; + hback-porch = <40>; + hsync-len = <48>; + vback-porch = <30>; + vfront-porch = <13>; + vsync-len = <3>; + hsync-active = <0>; + vsync-active = <0>; + de-active = <1>; + pixelclk-active = <0>; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/overlays/BB-BONE-LCD4-01-00A1.dts b/arch/arm/boot/dts/overlays/BB-BONE-LCD4-01-00A1.dts new file mode 100644 index 0000000000000..ad6beadcc21f8 --- /dev/null +++ b/arch/arm/boot/dts/overlays/BB-BONE-LCD4-01-00A1.dts @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-BONE-LCD4-01-00A1.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P9_12_pinmux { status = "disabled"; }; /* P9_12: gpmc_ben1.gpio1_28, INPUT | PULLDIS | MODE7 */ + + P9_14_pinmux { status = "disabled"; }; /* P9_14: gpmc_a2.ehrpwm1a */ + + P9_27_pinmux { status = "disabled"; }; /* P9_27: mcasp0_fsr.gpio3_19 */ + + P8_45_pinmux { status = "disabled"; }; /* P8_45: lcd_data0.lcd_data0 */ + P8_46_pinmux { status = "disabled"; }; /* P8_46: lcd_data1.lcd_data1 */ + P8_43_pinmux { status = "disabled"; }; /* P8_43: lcd_data2.lcd_data2 */ + P8_44_pinmux { status = "disabled"; }; /* P8_44: lcd_data3.lcd_data3 */ + P8_41_pinmux { status = "disabled"; }; /* P8_41: lcd_data4.lcd_data4 */ + P8_42_pinmux { status = "disabled"; }; /* P8_42: lcd_data5.lcd_data5 */ + P8_39_pinmux { status = "disabled"; }; /* P8_39: lcd_data6.lcd_data6 */ + P8_40_pinmux { status = "disabled"; }; /* P8_40: lcd_data7.lcd_data7 */ + P8_37_pinmux { status = "disabled"; }; /* P8_37: lcd_data8.lcd_data8 */ + P8_38_pinmux { status = "disabled"; }; /* P8_38: lcd_data9.lcd_data9 */ + P8_36_pinmux { status = "disabled"; }; /* P8_36: lcd_data10.lcd_data10 */ + P8_34_pinmux { status = "disabled"; }; /* P8_34: lcd_data11.lcd_data11 */ + P8_35_pinmux { status = "disabled"; }; /* P8_35: lcd_data12.lcd_data12 */ + P8_33_pinmux { status = "disabled"; }; /* P8_33: lcd_data13.lcd_data13 */ + P8_31_pinmux { status = "disabled"; }; /* P8_31: lcd_data14.lcd_data14 */ + P8_32_pinmux { status = "disabled"; }; /* P8_32: lcd_data15.lcd_data15 */ + + P8_27_pinmux { status = "disabled"; }; /* P8_27: lcd_vsync.lcd_vsync */ + P8_29_pinmux { status = "disabled"; }; /* P8_29: lcd_hsync.lcd_hsync */ + P8_28_pinmux { status = "disabled"; }; /* P8_28: lcd_pclk.lcd_pclk */ + P8_30_pinmux { status = "disabled"; }; /* P8_30: lcd_ac_bias_en.lcd_ac_bias_en */ + + P9_15_pinmux { status = "disabled"; }; /* P9_15: gpmc_a0.gpio1_16 */ + P9_23_pinmux { status = "disabled"; }; /* P9_23: gpmc_a1.gpio1_17 */ + P9_16_pinmux { status = "disabled"; }; /* P9_16: gpmc_a3.gpio1_19 */ + P9_30_pinmux { status = "disabled"; }; /* P9_30: mcasp0_axr0.gpio3_16 */ + P9_24_pinmux { status = "disabled"; }; /* P9_24: uart1_txd.gpio0_15 */ +}; + +&am33xx_pinmux { + bb_lcd_led_pins: pinmux_bb_lcd_led_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_BEN1, PIN_INPUT, MUX_MODE7) /* P9_12: gpmc_ben1.gpio1_28, INPUT | PULLDIS | MODE7 */ + >; + }; + + bb_lcd_pwm_backlight_pins: pinmux_bb_lcd_pwm_backlight_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_A2, PIN_OUTPUT_PULLDOWN, MUX_MODE6) /* P9_14: gpmc_a2.ehrpwm1a */ + >; + }; + + bb_lcd_lcd_pins: pinmux_bb_lcd_lcd_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MCASP0_FSR, PIN_OUTPUT_PULLUP, MUX_MODE7) /* P9_27: mcasp0_fsr.gpio3_19 */ + + AM33XX_PADCONF(AM335X_PIN_LCD_DATA0, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA1, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA2, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA3, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA4, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA5, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA6, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA7, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA8, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA9, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA10, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA11, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA12, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA13, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA14, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA15, PIN_OUTPUT, MUX_MODE0) + + AM33XX_PADCONF(AM335X_PIN_LCD_VSYNC, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_HSYNC, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_PCLK, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_AC_BIAS_EN, PIN_OUTPUT, MUX_MODE0) + >; + }; + + bb_lcd_keymap_pins: pinmux_bb_lcd_keymap_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_A0, PIN_INPUT, MUX_MODE7) /* P9_15: gpmc_a0.gpio1_16 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_A1, PIN_INPUT, MUX_MODE7) /* P9_23: gpmc_a1.gpio1_17 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_A3, PIN_INPUT, MUX_MODE7) /* P9_16: gpmc_a3.gpio1_19 */ + AM33XX_PADCONF(AM335X_PIN_MCASP0_AXR0, PIN_INPUT, MUX_MODE7) /* P9_30: mcasp0_axr0.gpio3_16 */ + AM33XX_PADCONF(AM335X_PIN_UART1_TXD, PIN_INPUT, MUX_MODE7) /* P9_24: uart1_txd.gpio0_15 */ + >; + }; +}; + +&epwmss1 { + status = "okay"; +}; + +&ehrpwm1 { + pinctrl-names = "default"; + pinctrl-0 = <&bb_lcd_pwm_backlight_pins>; + status = "okay"; +}; + +&lcdc { + status = "okay"; + + blue-and-red-wiring = "straight"; + + //FIXME - LCD doesn't init... + //port { + // lcdc_0: endpoint@0 { + // remote-endpoint = <&panel_0>; + // }; + //}; +}; + +&tscadc { + status = "okay"; + tsc { + ti,wires = <4>; + ti,x-plate-resistance = <200>; + ti,coordinate-readouts = <5>; + ti,wire-config = <0x00 0x11 0x22 0x33>; + ti,charge-delay = <0x400>; + }; + + adc { + ti,adc-channels = <4 5 6 7>; + }; +}; + +&{/} { + backlight: backlight { + status = "okay"; + compatible = "pwm-backlight"; + pwms = <&ehrpwm1 0 500000 0>; + brightness-levels = < + 0 1 2 3 4 5 6 7 8 9 + 10 11 12 13 14 15 16 17 18 19 + 20 21 22 23 24 25 26 27 28 29 + 30 31 32 33 34 35 36 37 38 39 + 40 41 42 43 44 45 46 47 48 49 + 50 51 52 53 54 55 56 57 58 59 + 60 61 62 63 64 65 66 67 68 69 + 70 71 72 73 74 75 76 77 78 79 + 80 81 82 83 84 85 86 87 88 89 + 90 91 92 93 94 95 96 97 98 99 + 100 + >; + default-brightness-level = <100>; + }; + + panel { + status = "okay"; + compatible = "ti,tilcdc,panel"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_lcd_lcd_pins>; + backlight = <&backlight>; + + //FIXME - LCD doesn't init... + //port { + // panel_0: endpoint@0 { + // remote-endpoint = <&lcdc_0>; + // }; + //}; + + panel-info { + ac-bias = <255>; + ac-bias-intrpt = <0>; + dma-burst-sz = <16>; + bpp = <16>; + fdd = <0x80>; + sync-edge = <0>; + sync-ctrl = <1>; + raster-order = <0>; + fifo-th = <0>; + }; + display-timings { + native-mode = <&timing0>; + /* www.newhavendisplay.com/app_notes/OTA5180A.pdf */ + timing0: 480x272 { + clock-frequency = <9200000>; + hactive = <480>; + vactive = <272>; + hfront-porch = <8>; + hback-porch = <47>; + hsync-len = <41>; + vback-porch = <2>; + vfront-porch = <3>; + vsync-len = <10>; + hsync-active = <0>; + vsync-active = <0>; + de-active = <1>; + pixelclk-active = <0>; + }; + }; + }; + + gpio-leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_lcd_led_pins>; + + led-ld0 { + label = "lcd:green:usr0"; + gpios = <&gpio1 28 0>; + linux,default-trigger = "heartbeat"; + default-state = "off"; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_lcd_keymap_pins>; + + button-1 { + debounce_interval = <50>; + linux,code = <105>; + label = "left"; + gpios = <&gpio1 16 0x1>; + gpio-key,wakeup; + autorepeat; + }; + button-2 { + debounce_interval = <50>; + linux,code = <106>; + label = "right"; + gpios = <&gpio1 17 0x1>; + gpio-key,wakeup; + autorepeat; + }; + button-3 { + debounce_interval = <50>; + linux,code = <103>; + label = "up"; + gpios = <&gpio1 19 0x1>; + gpio-key,wakeup; + autorepeat; + }; + button-4 { + debounce_interval = <50>; + linux,code = <108>; + label = "down"; + gpios = <&gpio3 16 0x1>; + gpio-key,wakeup; + autorepeat; + }; + button-5 { + debounce_interval = <50>; + linux,code = <28>; + label = "enter"; + gpios = <&gpio0 15 0x1>; + gpio-key,wakeup; + }; + }; +}; diff --git a/arch/arm/boot/dts/overlays/BB-BONE-NH7C-01-A0.dts b/arch/arm/boot/dts/overlays/BB-BONE-NH7C-01-A0.dts new file mode 100644 index 0000000000000..be9dfd135be50 --- /dev/null +++ b/arch/arm/boot/dts/overlays/BB-BONE-NH7C-01-A0.dts @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-BONE-NH7C-01-A0.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P8_45_pinmux { status = "disabled"; }; /* lcd: lcd_data0 */ + P8_46_pinmux { status = "disabled"; }; /* lcd: lcd_data1 */ + P8_43_pinmux { status = "disabled"; }; /* lcd: lcd_data2 */ + P8_44_pinmux { status = "disabled"; }; /* lcd: lcd_data3 */ + P8_41_pinmux { status = "disabled"; }; /* lcd: lcd_data4 */ + P8_42_pinmux { status = "disabled"; }; /* lcd: lcd_data5 */ + P8_39_pinmux { status = "disabled"; }; /* lcd: lcd_data6 */ + P8_40_pinmux { status = "disabled"; }; /* lcd: lcd_data7 */ + P8_37_pinmux { status = "disabled"; }; /* lcd: lcd_data8 */ + P8_38_pinmux { status = "disabled"; }; /* lcd: lcd_data9 */ + P8_36_pinmux { status = "disabled"; }; /* lcd: lcd_data10 */ + P8_34_pinmux { status = "disabled"; }; /* lcd: lcd_data11 */ + P8_35_pinmux { status = "disabled"; }; /* lcd: lcd_data12 */ + P8_33_pinmux { status = "disabled"; }; /* lcd: lcd_data13 */ + P8_31_pinmux { status = "disabled"; }; /* lcd: lcd_data14 */ + P8_32_pinmux { status = "disabled"; }; /* lcd: lcd_data15 */ + + P8_15_pinmux { status = "disabled"; }; /* gpmc_ad15.lcd_data16 */ + P8_16_pinmux { status = "disabled"; }; /* gpmc_ad14.lcd_data17 */ + P8_11_pinmux { status = "disabled"; }; /* gpmc_ad13.lcd_data18 */ + P8_12_pinmux { status = "disabled"; }; /* gpmc_ad12.lcd_data19 */ + P8_17_pinmux { status = "disabled"; }; /* gpmc_ad11.lcd_data20 */ + P8_14_pinmux { status = "disabled"; }; /* gpmc_ad10.lcd_data21 */ + P8_13_pinmux { status = "disabled"; }; /* gpmc_ad9.lcd_data22 */ + P8_19_pinmux { status = "disabled"; }; /* gpmc_ad8.lcd_data23 */ + + P8_27_pinmux { status = "disabled"; }; /* lcd: lcd_vsync */ + P8_29_pinmux { status = "disabled"; }; /* lcd: lcd_hsync */ + P8_28_pinmux { status = "disabled"; }; /* lcd: lcd_pclk */ + P8_30_pinmux { status = "disabled"; }; /* lcd: lcd_ac_bias_en */ + + P8_18_pinmux { status = "disabled"; }; /* lcd: enable */ + + P9_14_pinmux { status = "disabled"; }; /* pwm: ehrpwm1a */ + + P9_27_pinmux { status = "disabled"; }; /* ft5336: gpio3_15 */ +}; + +&am33xx_pinmux { + bb_lcd_pwm_backlight_pins: pinmux_bb_lcd_pwm_backlight_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_A2, PIN_OUTPUT_PULLDOWN, MUX_MODE6) /* P9_14: gpmc_a2.ehrpwm1a */ + >; + }; + + bb_lcd_lcd_pins: pinmux_bb_lcd_lcd_pins { + pinctrl-single,pins = < + /*LCD enable */ + AM33XX_PADCONF(AM335X_PIN_GPMC_CLK, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gpmc_clk_mux0.gpio2_1 */ + + AM33XX_PADCONF(AM335X_PIN_LCD_DATA0, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA1, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA2, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA3, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA4, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA5, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA6, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA7, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA8, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA9, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA10, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA11, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA12, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA13, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA14, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA15, PIN_OUTPUT, MUX_MODE0) + + AM33XX_PADCONF(AM335X_PIN_GPMC_AD15, PIN_OUTPUT, MUX_MODE1) /* P8_15: gpmc_ad15.lcd_data16 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD14, PIN_OUTPUT, MUX_MODE1) /* P8_16: gpmc_ad14.lcd_data17 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD13, PIN_OUTPUT, MUX_MODE1) /* P8_11: gpmc_ad13.lcd_data18 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD12, PIN_OUTPUT, MUX_MODE1) /* P8_12: gpmc_ad12.lcd_data19 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD11, PIN_OUTPUT, MUX_MODE1) /* P8_17: gpmc_ad11.lcd_data20 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD10, PIN_OUTPUT, MUX_MODE1) /* P8_14: gpmc_ad10.lcd_data21 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD9, PIN_OUTPUT, MUX_MODE1) /* P8_13: gpmc_ad9.lcd_data22 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD8, PIN_OUTPUT, MUX_MODE1) /* P8_19: gpmc_ad8.lcd_data23 */ + + AM33XX_PADCONF(AM335X_PIN_LCD_VSYNC, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_HSYNC, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_PCLK, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_AC_BIAS_EN, PIN_OUTPUT, MUX_MODE0) + >; + }; + + edt_ft5x06_pins: pinmux_edt_ft5x06_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MCASP0_FSR, PIN_INPUT_PULLDOWN, MUX_MODE7) /* mcasp0_fsr.gpio3_19 */ + >; + }; +}; + +&epwmss1 { + status = "okay"; +}; + +&ehrpwm1 { + pinctrl-names = "default"; + pinctrl-0 = <&bb_lcd_pwm_backlight_pins>; + status = "okay"; +}; + +&lcdc { + status = "okay"; + + blue-and-red-wiring = "crossed"; + + //FIXME - LCD doesn't init... + //port { + // lcdc_0: endpoint@0 { + // remote-endpoint = <&panel_0>; + // }; + //}; +}; + +&i2c2 { + status = "okay"; + + clock-frequency = <100000>; + + #address-cells = <1>; + #size-cells = <0>; + + edt-ft5x06@38 { + status = "okay"; + compatible = "edt,edt-ft5406"; + reg = <0x38>; + pinctrl-names = "default"; + pinctrl-0 = <&edt_ft5x06_pins>; + interrupt-parent = <&gpio3>; + interrupts = <19 0>; + //reset-gpios = <&gpio3 14 GPIO_ACTIVE_LOW>; + + touchscreen-size-x = <800>; + touchscreen-size-y = <480>; + //touchscreen-swapped-x-y; + }; +}; + +&{/} { + backlight: backlight { + status = "okay"; + compatible = "pwm-backlight"; + pwms = <&ehrpwm1 0 500000 0>; + brightness-levels = < + 0 1 2 3 4 5 6 7 8 9 + 10 11 12 13 14 15 16 17 18 19 + 20 21 22 23 24 25 26 27 28 29 + 30 31 32 33 34 35 36 37 38 39 + 40 41 42 43 44 45 46 47 48 49 + 50 51 52 53 54 55 56 57 58 59 + 60 61 62 63 64 65 66 67 68 69 + 70 71 72 73 74 75 76 77 78 79 + 80 81 82 83 84 85 86 87 88 89 + 90 91 92 93 94 95 96 97 98 99 + 100 + >; + default-brightness-level = <100>; + }; + + /* NHD-7.0-800480EF-ATXL# */ + panel { + status = "okay"; + compatible = "ti,tilcdc,panel"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_lcd_lcd_pins>; + backlight = <&backlight>; + enable-gpios = <&gpio2 1 0>; + + //FIXME - LCD doesn't init... + //port { + // panel_0: endpoint@0 { + // remote-endpoint = <&lcdc_0>; + // }; + //}; + + panel-info { + ac-bias = <255>; + ac-bias-intrpt = <0>; + dma-burst-sz = <16>; + bpp = <32>; + fdd = <0x80>; + tft-alt-mode = <0>; + stn-565-mode = <0>; + mono-8bit-mode = <0>; + sync-edge = <0>; + sync-ctrl = <0>; + raster-order = <0>; + fifo-th = <0>; + }; + + display-timings { + native-mode = <&timing0>; + timing0: 800x480 { + clock-frequency = <45000000>; + hactive = <800>; + vactive = <480>; + hfront-porch = <40>; + hback-porch = <40>; + hsync-len = <48>; + vback-porch = <29>; + vfront-porch = <13>; + vsync-len = <3>; + hsync-active = <0>; + vsync-active = <0>; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/overlays/BB-BONE-eMMC1-01-00A0.dts b/arch/arm/boot/dts/overlays/BB-BONE-eMMC1-01-00A0.dts new file mode 100644 index 0000000000000..edc70070f278f --- /dev/null +++ b/arch/arm/boot/dts/overlays/BB-BONE-eMMC1-01-00A0.dts @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-BONE-eMMC1-01-00A0.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P8_21_pinmux { status = "disabled"; }; /* mmc1_clk */ + P8_20_pinmux { status = "disabled"; }; /* mmc1_cmd */ + P8_25_pinmux { status = "disabled"; }; /* mmc1_dat0 */ + P8_24_pinmux { status = "disabled"; }; /* mmc1_dat1 */ + P8_05_pinmux { status = "disabled"; }; /* mmc1_dat2 */ + P8_06_pinmux { status = "disabled"; }; /* mmc1_dat3 */ + P8_23_pinmux { status = "disabled"; }; /* mmc1_dat4 */ + P8_22_pinmux { status = "disabled"; }; /* mmc1_dat5 */ + P8_03_pinmux { status = "disabled"; }; /* mmc1_dat6 */ + P8_04_pinmux { status = "disabled"; }; /* mmc1_dat7 */ +}; + +&am33xx_pinmux { + emmc_pins: pinmux_emmc_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_CSN1, PIN_INPUT_PULLUP, MUX_MODE2) /* gpmc_csn1.mmc1_clk */ + AM33XX_PADCONF(AM335X_PIN_GPMC_CSN2, PIN_INPUT_PULLUP, MUX_MODE2) /* gpmc_csn2.mmc1_cmd */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD0, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad0.mmc1_dat0 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD1, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad1.mmc1_dat1 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD2, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad2.mmc1_dat2 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD3, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad3.mmc1_dat3 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD4, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad4.mmc1_dat4 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD5, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad5.mmc1_dat5 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD6, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad6.mmc1_dat6 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_AD7, PIN_INPUT_PULLUP, MUX_MODE1) /* gpmc_ad7.mmc1_dat7 */ + >; + }; +}; + +&mmc2 { + vmmc-supply = <&vmmcsd_fixed>; + pinctrl-names = "default"; + pinctrl-0 = <&emmc_pins>; + bus-width = <8>; + status = "okay"; + non-removable; +}; diff --git a/arch/arm/boot/dts/overlays/BB-CAPE-DISP-CT4-00A0.dts b/arch/arm/boot/dts/overlays/BB-CAPE-DISP-CT4-00A0.dts new file mode 100644 index 0000000000000..c47fa0b1f8713 --- /dev/null +++ b/arch/arm/boot/dts/overlays/BB-CAPE-DISP-CT4-00A0.dts @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-CAPE-DISP-CT4-00A0.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P8_45_pinmux { status = "disabled"; }; /* lcd: lcd_data0 */ + P8_46_pinmux { status = "disabled"; }; /* lcd: lcd_data1 */ + P8_43_pinmux { status = "disabled"; }; /* lcd: lcd_data2 */ + P8_44_pinmux { status = "disabled"; }; /* lcd: lcd_data3 */ + P8_41_pinmux { status = "disabled"; }; /* lcd: lcd_data4 */ + P8_42_pinmux { status = "disabled"; }; /* lcd: lcd_data5 */ + P8_39_pinmux { status = "disabled"; }; /* lcd: lcd_data6 */ + P8_40_pinmux { status = "disabled"; }; /* lcd: lcd_data7 */ + P8_37_pinmux { status = "disabled"; }; /* lcd: lcd_data8 */ + P8_38_pinmux { status = "disabled"; }; /* lcd: lcd_data9 */ + P8_36_pinmux { status = "disabled"; }; /* lcd: lcd_data10 */ + P8_34_pinmux { status = "disabled"; }; /* lcd: lcd_data11 */ + P8_35_pinmux { status = "disabled"; }; /* lcd: lcd_data12 */ + P8_33_pinmux { status = "disabled"; }; /* lcd: lcd_data13 */ + P8_31_pinmux { status = "disabled"; }; /* lcd: lcd_data14 */ + P8_32_pinmux { status = "disabled"; }; /* lcd: lcd_data15 */ + + P8_27_pinmux { status = "disabled"; }; /* lcd: lcd_vsync */ + P8_29_pinmux { status = "disabled"; }; /* lcd: lcd_hsync */ + P8_28_pinmux { status = "disabled"; }; /* lcd: lcd_pclk */ + P8_30_pinmux { status = "disabled"; }; /* lcd: lcd_ac_bias_en */ + + P9_28_pinmux { status = "disabled"; }; /* pwm: eCAP2_in_PWM2_out */ + + P9_29_pinmux { status = "disabled"; }; /* ft5336: gpio3_15 */ + P9_31_pinmux { status = "disabled"; }; /* ft5336: gpio3_14 */ +}; + +&am33xx_pinmux { + bb_lcd_pwm_backlight_pins: pinmux_bb_lcd_pwm_backlight_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKR, PIN_OUTPUT_PULLDOWN, MUX_MODE4) /* mcasp0_ahclkr.eCAP2_in_PWM2_out */ + >; + }; + + bb_lcd_lcd_pins: pinmux_bb_lcd_lcd_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_LCD_DATA0, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA1, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA2, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA3, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA4, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA5, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA6, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA7, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA8, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA9, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA10, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA11, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA12, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA13, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA14, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA15, PIN_OUTPUT, MUX_MODE0) + + AM33XX_PADCONF(AM335X_PIN_LCD_VSYNC, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_HSYNC, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_PCLK, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_AC_BIAS_EN, PIN_OUTPUT, MUX_MODE0) + >; + }; + + edt_ft5336_ts_pins: pinmux_edt_ft5336_ts_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MCASP0_FSX, PIN_INPUT_PULLDOWN, MUX_MODE7) /* mcasp0_fsx.gpio3_15 */ + AM33XX_PADCONF(AM335X_PIN_MCASP0_ACLKX, PIN_INPUT_PULLDOWN, MUX_MODE7) /* mcasp0_aclkx.gpio3_14 */ + >; + }; +}; + +&epwmss2 { + status = "okay"; +}; + +&ecap2 { + pinctrl-names = "default"; + pinctrl-0 = <&bb_lcd_pwm_backlight_pins>; + status = "okay"; +}; + +&lcdc { + status = "okay"; + + blue-and-red-wiring = "straight"; + + //FIXME - LCD doesn't init... + //port { + // lcdc_0: endpoint@0 { + // remote-endpoint = <&panel_0>; + // }; + //}; +}; + +&i2c2 { + status = "okay"; + + /* this is the configuration part */ + clock-frequency = <100000>; + + #address-cells = <1>; + #size-cells = <0>; + + edt-ft5336@38 { + status = "okay"; + compatible = "edt,edt-ft5336", "edt,edt-ft5306", "edt,edt-ft5x06"; + reg = <0x38>; + pinctrl-names = "default"; + pinctrl-0 = <&edt_ft5336_ts_pins>; + interrupt-parent = <&gpio3>; + interrupts = <15 0>; + reset-gpios = <&gpio3 14 GPIO_ACTIVE_LOW>; + + touchscreen-size-x = <272>; + touchscreen-size-y = <480>; + touchscreen-swapped-x-y; + }; +}; + +&{/} { + backlight: backlight { + status = "okay"; + compatible = "pwm-backlight"; + pwms = <&ecap2 0 500000 0>; + brightness-levels = < + 0 1 2 3 4 5 6 7 8 9 + 10 11 12 13 14 15 16 17 18 19 + 20 21 22 23 24 25 26 27 28 29 + 30 31 32 33 34 35 36 37 38 39 + 40 41 42 43 44 45 46 47 48 49 + 50 51 52 53 54 55 56 57 58 59 + 60 61 62 63 64 65 66 67 68 69 + 70 71 72 73 74 75 76 77 78 79 + 80 81 82 83 84 85 86 87 88 89 + 90 91 92 93 94 95 96 97 98 99 + 100 + >; + default-brightness-level = <50>; + }; + + panel { + status = "okay"; + compatible = "ti,tilcdc,panel"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_lcd_lcd_pins>; + backlight = <&backlight>; + + //FIXME - LCD doesn't init... + //port { + // panel_0: endpoint@0 { + // remote-endpoint = <&lcdc_0>; + // }; + //}; + + panel-info { + ac-bias = <255>; + ac-bias-intrpt = <0>; + dma-burst-sz = <16>; + bpp = <16>; + fdd = <0x80>; + tft-alt-mode = <0>; + stn-565-mode = <0>; + mono-8bit-mode = <0>; + sync-edge = <0>; + sync-ctrl = <1>; + raster-order = <0>; + fifo-th = <0>; + }; + /* ILI6480 */ + display-timings { + native-mode = <&timing0>; + timing0: 480x272 { + clock-frequency = <9000000>; + hactive = <480>; + vactive = <272>; + hfront-porch = <5>; + hback-porch = <40>; + hsync-len = <1>; + vback-porch = <8>; + vfront-porch = <8>; + vsync-len = <1>; + hsync-active = <0>; + vsync-active = <0>; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/overlays/BB-HDMI-TDA998x-00A0.dts b/arch/arm/boot/dts/overlays/BB-HDMI-TDA998x-00A0.dts new file mode 100644 index 0000000000000..1c0eaa74365eb --- /dev/null +++ b/arch/arm/boot/dts/overlays/BB-HDMI-TDA998x-00A0.dts @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-HDMI-TDA998x-00A0.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P9_25_pinmux { status = "disabled"; }; /* mcasp0_ahclkx */ + P9_28_pinmux { status = "disabled"; }; /* mcasp0_axr2 */ + P9_29_pinmux { status = "disabled"; }; /* mcasp0_fsx */ + P9_31_pinmux { status = "disabled"; }; /* mcasp0_aclkx */ + P8_45_pinmux { status = "disabled"; }; /* lcd_data0 */ + P8_46_pinmux { status = "disabled"; }; /* lcd_data1 */ + P8_43_pinmux { status = "disabled"; }; /* lcd_data2 */ + P8_44_pinmux { status = "disabled"; }; /* lcd_data3 */ + P8_41_pinmux { status = "disabled"; }; /* lcd_data4 */ + P8_42_pinmux { status = "disabled"; }; /* lcd_data5 */ + P8_39_pinmux { status = "disabled"; }; /* lcd_data6 */ + P8_40_pinmux { status = "disabled"; }; /* lcd_data7 */ + P8_37_pinmux { status = "disabled"; }; /* lcd_data8 */ + P8_38_pinmux { status = "disabled"; }; /* lcd_data9 */ + P8_36_pinmux { status = "disabled"; }; /* lcd_data10 */ + P8_34_pinmux { status = "disabled"; }; /* lcd_data11 */ + P8_35_pinmux { status = "disabled"; }; /* lcd_data12 */ + P8_33_pinmux { status = "disabled"; }; /* lcd_data13 */ + P8_31_pinmux { status = "disabled"; }; /* lcd_data14 */ + P8_32_pinmux { status = "disabled"; }; /* lcd_data15 */ + P8_27_pinmux { status = "disabled"; }; /* lcd_vsync */ + P8_29_pinmux { status = "disabled"; }; /* lcd_hsync */ + P8_28_pinmux { status = "disabled"; }; /* lcd_pclk */ + P8_30_pinmux { status = "disabled"; }; /* lcd_ac_bias_en */ +}; + +&am33xx_pinmux { + nxp_hdmi_bonelt_pins: nxp_hdmi_bonelt_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_OUTPUT_PULLUP, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA0, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA1, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA2, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA3, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA4, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA5, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA6, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA7, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA8, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA9, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA10, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA11, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA12, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA13, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA14, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA15, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_VSYNC, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_HSYNC, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_PCLK, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_AC_BIAS_EN, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + >; + }; + + nxp_hdmi_bonelt_off_pins: nxp_hdmi_bonelt_off_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_OUTPUT_PULLDOWN, MUX_MODE7) + >; + }; + + mcasp0_pins: mcasp0_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKX, PIN_INPUT_PULLUP, MUX_MODE0) /* mcasp0_ahcklx.mcasp0_ahclkx */ + AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKR, PIN_OUTPUT_PULLDOWN, MUX_MODE2) /* mcasp0_ahclkr.mcasp0_axr2*/ + AM33XX_PADCONF(AM335X_PIN_MCASP0_FSX, PIN_OUTPUT_PULLUP, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_MCASP0_ACLKX, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_GPMC_A11, PIN_OUTPUT_PULLDOWN, MUX_MODE7) /* gpmc_a11.GPIO1_27 */ + >; + }; +}; + +&lcdc { + status = "okay"; + + /* If you want to get 24 bit RGB and 16 BGR mode instead of + * current 16 bit RGB and 24 BGR modes, set the propety + * below to "crossed" and uncomment the video-ports -property + * in tda19988 node. + */ + blue-and-red-wiring = "straight"; + + port { + lcdc_0: endpoint@0 { + remote-endpoint = <&hdmi_0>; + }; + }; +}; + +&i2c0 { + #address-cells = <1>; + #size-cells = <0>; + + tda19988: tda19988@70 { + compatible = "nxp,tda998x"; + reg = <0x70>; + nxp,calib-gpios = <&gpio1 25 0>; + interrupts-extended = <&gpio1 25 IRQ_TYPE_LEVEL_LOW>; + + pinctrl-names = "default", "off"; + pinctrl-0 = <&nxp_hdmi_bonelt_pins>; + pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>; + + /* Convert 24bit BGR to RGB, e.g. cross red and blue wiring */ + /* video-ports = <0x234501>; */ + + #sound-dai-cells = <0>; + audio-ports = < TDA998x_I2S 0x03>; + + ports { + port@0 { + hdmi_0: endpoint@0 { + remote-endpoint = <&lcdc_0>; + }; + }; + }; + }; +}; + +&mcasp0 { + #sound-dai-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&mcasp0_pins>; + status = "okay"; + op-mode = <0>; /* MCASP_IIS_MODE */ + tdm-slots = <2>; + serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */ + 0 0 1 0 + >; + tx-num-evt = <32>; + rx-num-evt = <32>; +}; + +&{/} { + clk_mcasp0_fixed: clk_mcasp0_fixed { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <24576000>; + }; + + clk_mcasp0: clk_mcasp0 { + #clock-cells = <0>; + compatible = "gpio-gate-clock"; + clocks = <&clk_mcasp0_fixed>; + enable-gpios = <&gpio1 27 0>; /* BeagleBone Black Clk enable on GPIO1_27 */ + }; + + sound { + compatible = "simple-audio-card"; + simple-audio-card,name = "TI BeagleBone Black"; + simple-audio-card,format = "i2s"; + simple-audio-card,bitclock-master = <&dailink0_master>; + simple-audio-card,frame-master = <&dailink0_master>; + + dailink0_master: simple-audio-card,cpu { + sound-dai = <&mcasp0>; + clocks = <&clk_mcasp0>; + }; + + simple-audio-card,codec { + sound-dai = <&tda19988>; + }; + }; +}; diff --git a/arch/arm/boot/dts/overlays/BB-I2C1-MCP7940X-00A0.dts b/arch/arm/boot/dts/overlays/BB-I2C1-MCP7940X-00A0.dts new file mode 100644 index 0000000000000..de62adf18cd46 --- /dev/null +++ b/arch/arm/boot/dts/overlays/BB-I2C1-MCP7940X-00A0.dts @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2015 Robert Nelson + */ + +/dts-v1/; +/plugin/; + +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-I2C1-MCP7940X-00A0.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P9_17_pinmux { status = "disabled"; }; /* spi0_d1.i2c1_sda */ + P9_18_pinmux { status = "disabled"; }; /* spi0_cs0.i2c1_scl */ + P8_26_pinmux { status = "disabled"; }; /* rtc: gpio1_29 */ +}; + +&{/} { + aliases { + rtc0 = &extrtc; + /* find /sys/firmware/devicetree/ | grep rtc@ */ + rtc1 = "/ocp/interconnect@44c00000/segment@200000/target-module@3e000/rtc@0"; + }; + + gpio_keys { + compatible = "gpio-keys"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_gpio1_29_pins>; + + rtc_mfp@1 { + label = "rtc_mfp"; + gpios = <&gpio1 29 GPIO_ACTIVE_HIGH>; + linux,code = <143>; /* System Wake Up */ + gpio-key,wakeup; + }; + }; +}; + +&am33xx_pinmux { + bb_gpio1_29_pins: pinmux_bb_gpio1_29_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_CSN0, PIN_INPUT, MUX_MODE7) /* gpmc_csn0.gpio1_29 */ + >; + }; + + bb_i2c1_pins: pinmux_bb_i2c1_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_SPI0_D1, SLEWCTRL_SLOW | PIN_INPUT_PULLUP, MUX_MODE2) /* spi0_d1.i2c1_sda */ + AM33XX_PADCONF(AM335X_PIN_SPI0_CS0, SLEWCTRL_SLOW | PIN_INPUT_PULLUP, MUX_MODE2) /* spi0_cs0.i2c1_scl */ + >; + }; +}; + +&i2c1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_i2c1_pins>; + + clock-frequency = <100000>; + + #address-cells = <1>; + #size-cells = <0>; + + extrtc: mcp7940x@68 { + status = "okay"; + compatible = "microchip,mcp7940x"; + reg = <0x68>; + }; +}; diff --git a/arch/arm/boot/dts/overlays/BB-I2C1-RTC-DS3231.dts b/arch/arm/boot/dts/overlays/BB-I2C1-RTC-DS3231.dts new file mode 100644 index 0000000000000..5e82bc150140b --- /dev/null +++ b/arch/arm/boot/dts/overlays/BB-I2C1-RTC-DS3231.dts @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021 Sam Cohen + * + * Based on BB-I2C2-RTC-DS3231.dts: + * Copyright (C) 2019 Tomas Arturo Herrera Castro + * + * DTS file for DS3231 Real Time Clock, running on the I2C1 interface. Also see + * BB-I2C2-RTC-DS3231.dts to run this RTC on I2C2. + * + * Tested on BeagleBone Black Wireless + */ + +/dts-v1/; +/plugin/; + +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-I2C1-RTC-DS3231.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P9_17_pinmux { status = "disabled"; }; /* spi0_d1.i2c1_sda */ + P9_18_pinmux { status = "disabled"; }; /* spi0_cs0.i2c1_scl */ +}; + +&{/} { + aliases { + rtc0 = &extrtc; + /* find /sys/firmware/devicetree/ | grep rtc@ */ + rtc1 = "/ocp/interconnect@44c00000/segment@200000/target-module@3e000/rtc@0"; + }; +}; + +&am33xx_pinmux { + bb_i2c1_pins: pinmux_bb_i2c1_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_SPI0_D1, SLEWCTRL_SLOW | PIN_INPUT_PULLUP, MUX_MODE2) /* spi0_d1.i2c1_sda */ + AM33XX_PADCONF(AM335X_PIN_SPI0_CS0, SLEWCTRL_SLOW | PIN_INPUT_PULLUP, MUX_MODE2) /* spi0_cs0.i2c1_scl */ + >; + }; +}; + +&i2c1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_i2c1_pins>; + + clock-frequency = <100000>; + + #address-cells = <1>; + #size-cells = <0>; + + extrtc: ds3231@68 { + status = "okay"; + compatible = "maxim,ds3231"; + reg = <0x68>; + }; +}; diff --git a/arch/arm/boot/dts/overlays/BB-I2C1-RTC-PCF8563.dts b/arch/arm/boot/dts/overlays/BB-I2C1-RTC-PCF8563.dts new file mode 100644 index 0000000000000..a60a9f6313af2 --- /dev/null +++ b/arch/arm/boot/dts/overlays/BB-I2C1-RTC-PCF8563.dts @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2018 Robert Nelson + */ + +/dts-v1/; +/plugin/; + +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-I2C1-RTC-PCF8563.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P9_17_pinmux { status = "disabled"; }; /* spi0_d1.i2c1_sda */ + P9_18_pinmux { status = "disabled"; }; /* spi0_cs0.i2c1_scl */ +}; + +&{/} { + aliases { + rtc0 = &extrtc; + /* find /sys/firmware/devicetree/ | grep rtc@ */ + rtc1 = "/ocp/interconnect@44c00000/segment@200000/target-module@3e000/rtc@0"; + }; +}; + +&am33xx_pinmux { + bb_i2c1_pins: pinmux_bb_i2c1_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_SPI0_D1, SLEWCTRL_SLOW | PIN_INPUT_PULLUP, MUX_MODE2) /* spi0_d1.i2c1_sda */ + AM33XX_PADCONF(AM335X_PIN_SPI0_CS0, SLEWCTRL_SLOW | PIN_INPUT_PULLUP, MUX_MODE2) /* spi0_cs0.i2c1_scl */ + >; + }; +}; + +&i2c1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_i2c1_pins>; + + clock-frequency = <100000>; + + #address-cells = <1>; + #size-cells = <0>; + + extrtc: pcf8563@51 { + status = "okay"; + compatible = "nxp,pcf8563"; + reg = <0x51>; + }; +}; diff --git a/arch/arm/boot/dts/overlays/BB-I2C2-BME680.dts b/arch/arm/boot/dts/overlays/BB-I2C2-BME680.dts new file mode 100644 index 0000000000000..a16476a82ab9f --- /dev/null +++ b/arch/arm/boot/dts/overlays/BB-I2C2-BME680.dts @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 Robert Nelson + */ + +/dts-v1/; +/plugin/; + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-I2C2-BME680.kernel = __TIMESTAMP__; + }; +}; + +&i2c2 { + status = "okay"; + + clock-frequency = <100000>; + + #address-cells = <1>; + #size-cells = <0>; + + bme680@76 { + status = "okay"; + compatible = "bosch,bme680"; + reg = <0x76>; + }; +}; diff --git a/arch/arm/boot/dts/overlays/BB-I2C2-MPU6050.dts b/arch/arm/boot/dts/overlays/BB-I2C2-MPU6050.dts new file mode 100644 index 0000000000000..c0136221e905e --- /dev/null +++ b/arch/arm/boot/dts/overlays/BB-I2C2-MPU6050.dts @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 Robert Nelson + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-I2C2-MPU6050.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P9_12_pinmux { status = "disabled"; }; +}; + +&am33xx_pinmux { + mpu6050_pins: pinmux_mpu6050_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_BEN1, PIN_INPUT, MUX_MODE7) /* gpio1_28 */ + >; + }; +}; + +&i2c2 { + status = "okay"; + + clock-frequency = <400000>; + + #address-cells = <1>; + #size-cells = <0>; + + imu@68 { + pinctrl-names = "default"; + pinctrl-0 = <&mpu6050_pins>; + compatible = "invensense,mpu6050"; + reg = <0x68>; + interrupt-parent = <&gpio1>; + interrupts = <28 IRQ_TYPE_EDGE_RISING>; + }; +}; diff --git a/arch/arm/boot/dts/overlays/BB-LCD-ADAFRUIT-24-SPI1-00A0.dts b/arch/arm/boot/dts/overlays/BB-LCD-ADAFRUIT-24-SPI1-00A0.dts new file mode 100644 index 0000000000000..d4f9a6fe017a2 --- /dev/null +++ b/arch/arm/boot/dts/overlays/BB-LCD-ADAFRUIT-24-SPI1-00A0.dts @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 CircuitCo + * Copyright (C) 2018 Drew Fustini + * Copyright (C) 2019 Mark A. Yoder + * + * Adafruit 2.4" TFT LCD on SPI1 bus using tinydrm ili9341 driver + * + * DOCUMENTATION: + * -------------- + * This file was copied from src/arm/BB-SPIDEV1-00A0.dts and modified + * by Drew Fustini based on an exmample from David Lechner. + * Later modified by Mark A. Yoder for the 2.4" LCD. + * + * This is the Adafruit 2.4" TFT LCD: + * https://www.adafruit.com/product/2478 + * + * It should be connected to BeagleBone SPI1 bus: + * + * P9.16 <--> lite (pwm) [OPTIONAL] + * P9.23 <--> lite (gpio) [OPTIONAL] + * P9.25 <--> reset + * P9.27 <--> dc + * P9.28 <--> tft_cs + * P9.29 <--> miso + * P9.30 <--> mosi + * P9.31 <--> clk + * + * This overlay will load the mainline tinydrm ili9341 driver by David Lechner: + * https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/tiny/ili9341.c + * + * Tested with 4.19.59-ti-r26 kernel on Debian 10.1 image + * + * Run libdrm modetest for colorbar test based on instructions from: + * https://github.com/notro/tinydrm/wiki/Development#modetest + * + * modetest -M "ili9341" -c #this will display connector id + * modetest -M "ili9341" -s 28:128x160 #connector id and resolution + * # you should now see a color bar on the LCD + * + * Mailing list post with more information: + * https://groups.google.com/d/msg/beagleboard/GuMQIP_XCW0/b3lxbx_8AwAJ + * + * Discussion with notro on how to test tinydrm driver: + * https://github.com/notro/tinydrm/issues/1#issuecomment-367279037 + */ + +/dts-v1/; +/plugin/; + +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-LCD-ADAFRUIT-24-SPI1-00A0 = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P9_25_pinmux { status = "disabled"; }; /* lcd reset */ + P9_16_pinmux { status = "disabled"; }; /* lcd pwm backlight (OPTIONAL) */ + P9_27_pinmux { status = "disabled"; }; /* lcd dc */ + P9_28_pinmux { status = "disabled"; }; /* spi1_cs0 */ + P9_29_pinmux { status = "disabled"; }; /* spi1_d0 */ + P9_30_pinmux { status = "disabled"; }; /* spi1_d1 */ + P9_31_pinmux { status = "disabled"; }; /* spi1_sclk */ +}; + +&am33xx_pinmux { + /* default state has all gpios released and mode set to uart1 */ + /* See page 1446 of am35xx TRM */ + bb_spi1_pins: pinmux_bb_spi1_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MCASP0_ACLKX, PIN_INPUT, MUX_MODE3) /* mcasp0_aclkx.spi1_sclk */ + AM33XX_PADCONF(AM335X_PIN_MCASP0_FSX, PIN_INPUT, MUX_MODE3) /* mcasp0_fsx.spi1_d0 */ + AM33XX_PADCONF(AM335X_PIN_MCASP0_AXR0, PIN_INPUT, MUX_MODE3) /* mcasp0_axr0.spi1_d1 */ + AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKR, PIN_INPUT, MUX_MODE3) /* mcasp0_ahclkr.spi1_cs0 */ + AM33XX_PADCONF(AM335X_PIN_MCASP0_FSR, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gpio, dc */ + AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKX, PIN_OUTPUT_PULLUP, MUX_MODE7) /* gpio, reset */ + >; + }; + + backlight_pwm_pins: pinmux_backlight_pwm_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_A3, PIN_OUTPUT, MUX_MODE6) /* gpmc_a2.ehrpwm1b */ + >; + }; /* gpmc_a2.ehrpwm1b */ +}; + +&epwmss1 { + status = "okay"; +}; + +&ehrpwm1 { + pinctrl-names = "default"; + pinctrl-0 = <&backlight_pwm_pins>; + status = "okay"; +}; + +&spi1 { + #address-cells = <1>; + #size-cells = <0>; + + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_spi1_pins>; + + channel@0{ + status = "disabled"; + reg = <0>; + }; + + display@0{ + status = "okay"; + compatible = "adafruit,yx240qv29", "ilitek,ili9341"; + reg = <0>; + spi-max-frequency = <32000000>; + dc-gpios = <&gpio3 19 0>; // lcd dc P9.27 gpio3[19] + reset-gpios = <&gpio3 21 0>; // lcd reset P9.25 gpio3[21] + // backlight is optional + // choose either pwm or gpio control + //backlight = <&backlight_gpio>; // lcd lite P9.23 gpio1[17] + backlight = <&backlight_pwm>; // lcd lite P9.16 gpmc_a2.ehrpwm1b + // refer to https://elinux.org/Beagleboard:Cape_Expansion_Headers + // rotation is optional + rotation = <270>; + }; +}; + +&{/} { + bl_reg: backlight-regulator { + compatible = "regulator-fixed"; + regulator-name = "backlight"; + regulator-always-on; + regulator-boot-on; + }; + + /* backlight is optional */ + backlight_gpio: backlight_gpio { + compatible = "gpio-backlight"; + gpios = <&gpio1 17 GPIO_ACTIVE_HIGH>; + // connect lcd lite pin to P9.23 which is gpio1[17] + // refer to https://elinux.org/Beagleboard:Cape_Expansion_Headers + }; + + /* + * Turn the PWM backlight on by setting bl_power to 0: + * echo 0 > /sys/class/backlight/backlight_pwm/bl_power + */ + backlight_pwm: backlight_pwm { + // P9.16 <--> lite (pwm-backlight EHRPWM1B) + status = "okay"; + compatible = "pwm-backlight"; + pwms = <&ehrpwm1 1 500000 0>; // First digit: 0 for A side of pwm, 1 for B side + // 500000 is the PWM period in ns + // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/pwm/pwm.txt + brightness-levels = < + 0 1 2 3 4 5 6 7 8 9 + 10 11 12 13 14 15 16 17 18 19 + 20 21 22 23 24 25 26 27 28 29 + 30 31 32 33 34 35 36 37 38 39 + 40 41 42 43 44 45 46 47 48 49 + 50 51 52 53 54 55 56 57 58 59 + 60 61 62 63 64 65 66 67 68 69 + 70 71 72 73 74 75 76 77 78 79 + 80 81 82 83 84 85 86 87 88 89 + 90 91 92 93 94 95 96 97 98 99 + 100 + >; + default-brightness-level = <100>; + power-supply = <&bl_reg>; + }; +}; diff --git a/arch/arm/boot/dts/overlays/BB-NHDMI-TDA998x-00A0.dts b/arch/arm/boot/dts/overlays/BB-NHDMI-TDA998x-00A0.dts new file mode 100644 index 0000000000000..b448d30132405 --- /dev/null +++ b/arch/arm/boot/dts/overlays/BB-NHDMI-TDA998x-00A0.dts @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-NHDMI-TDA998x-00A0.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P8_45_pinmux { status = "disabled"; }; /* lcd_data0 */ + P8_46_pinmux { status = "disabled"; }; /* lcd_data1 */ + P8_43_pinmux { status = "disabled"; }; /* lcd_data2 */ + P8_44_pinmux { status = "disabled"; }; /* lcd_data3 */ + P8_41_pinmux { status = "disabled"; }; /* lcd_data4 */ + P8_42_pinmux { status = "disabled"; }; /* lcd_data5 */ + P8_39_pinmux { status = "disabled"; }; /* lcd_data6 */ + P8_40_pinmux { status = "disabled"; }; /* lcd_data7 */ + P8_37_pinmux { status = "disabled"; }; /* lcd_data8 */ + P8_38_pinmux { status = "disabled"; }; /* lcd_data9 */ + P8_36_pinmux { status = "disabled"; }; /* lcd_data10 */ + P8_34_pinmux { status = "disabled"; }; /* lcd_data11 */ + P8_35_pinmux { status = "disabled"; }; /* lcd_data12 */ + P8_33_pinmux { status = "disabled"; }; /* lcd_data13 */ + P8_31_pinmux { status = "disabled"; }; /* lcd_data14 */ + P8_32_pinmux { status = "disabled"; }; /* lcd_data15 */ + P8_27_pinmux { status = "disabled"; }; /* lcd_vsync */ + P8_29_pinmux { status = "disabled"; }; /* lcd_hsync */ + P8_28_pinmux { status = "disabled"; }; /* lcd_pclk */ + P8_30_pinmux { status = "disabled"; }; /* lcd_ac_bias_en */ +}; + +&am33xx_pinmux { + nxp_hdmi_bonelt_pins: nxp_hdmi_bonelt_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_OUTPUT_PULLUP, MUX_MODE7) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA0, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA1, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA2, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA3, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA4, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA5, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA6, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA7, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA8, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA9, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA10, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA11, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA12, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA13, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA14, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_DATA15, PIN_OUTPUT, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_VSYNC, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_HSYNC, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_PCLK, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + AM33XX_PADCONF(AM335X_PIN_LCD_AC_BIAS_EN, PIN_OUTPUT_PULLDOWN, MUX_MODE0) + >; + }; + + nxp_hdmi_bonelt_off_pins: nxp_hdmi_bonelt_off_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_XDMA_EVENT_INTR0, PIN_OUTPUT_PULLDOWN, MUX_MODE7) + >; + }; +}; + +&lcdc { + status = "okay"; + + /* If you want to get 24 bit RGB and 16 BGR mode instead of + * current 16 bit RGB and 24 BGR modes, set the propety + * below to "crossed" and uncomment the video-ports -property + * in tda19988 node. + */ + blue-and-red-wiring = "straight"; + + port { + lcdc_0: endpoint@0 { + remote-endpoint = <&hdmi_0>; + }; + }; +}; + +&i2c0 { + #address-cells = <1>; + #size-cells = <0>; + + tda19988: tda19988@70 { + compatible = "nxp,tda998x"; + reg = <0x70>; + nxp,calib-gpios = <&gpio1 25 0>; + interrupts-extended = <&gpio1 25 IRQ_TYPE_LEVEL_LOW>; + + pinctrl-names = "default", "off"; + pinctrl-0 = <&nxp_hdmi_bonelt_pins>; + pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>; + + /* Convert 24bit BGR to RGB, e.g. cross red and blue wiring */ + /* video-ports = <0x234501>; */ + + ports { + port@0 { + hdmi_0: endpoint@0 { + remote-endpoint = <&lcdc_0>; + }; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/overlays/BB-SPIDEV0-00A0.dts b/arch/arm/boot/dts/overlays/BB-SPIDEV0-00A0.dts new file mode 100644 index 0000000000000..3b5b06d8453df --- /dev/null +++ b/arch/arm/boot/dts/overlays/BB-SPIDEV0-00A0.dts @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 CircuitCo + * Virtual cape for SPI0 on connector pins P9.22 P9.21 P9.18 P9.17 + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-SPIDEV0-00A0.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P9_17_pinmux { status = "disabled"; }; /* P9_17 (A16) spi0_cs0.spi0_cs0 */ + P9_18_pinmux { status = "disabled"; }; /* P9_18 (B16) spi0_d1.spi0_d1 */ + P9_21_pinmux { status = "disabled"; }; /* P9_21 (B17) spi0_d0.spi0_d0 */ + P9_22_pinmux { status = "disabled"; }; /* P9_22 (A17) spi0_sclk.spi0_sclk */ +}; + +&am33xx_pinmux { + bb_spi0_pins: pinmux_bb_spi0_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_SPI0_SCLK, PIN_INPUT, MUX_MODE0) /* P9_22 (A17) spi0_sclk.spi0_sclk */ + AM33XX_PADCONF(AM335X_PIN_SPI0_D0, PIN_INPUT, MUX_MODE0) /* P9_21 (B17) spi0_d0.spi0_d0 */ + AM33XX_PADCONF(AM335X_PIN_SPI0_D1, PIN_INPUT, MUX_MODE0) /* P9_18 (B16) spi0_d1.spi0_d1 */ + AM33XX_PADCONF(AM335X_PIN_SPI0_CS0, PIN_INPUT, MUX_MODE0) /* P9_17 (A16) spi0_cs0.spi0_cs0 */ + >; + }; +}; + +&spi0 { + #address-cells = <1>; + #size-cells = <0>; + + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_spi0_pins>; + + /* + * Select the D0 pin as output and D1 as + * input. The default is D0 as input and + * D1 as output. + */ + //ti,pindir-d0-out-d1-in; + + channel@0 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "rohm,dh2228fv"; + symlink = "bone/spi/0.0"; + + reg = <0>; + spi-max-frequency = <16000000>; + spi-cpha; + }; + + channel@1 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "rohm,dh2228fv"; + symlink = "bone/spi/0.1"; + + reg = <1>; + spi-max-frequency = <16000000>; + }; +}; diff --git a/arch/arm/boot/dts/overlays/BB-SPIDEV1-00A0.dts b/arch/arm/boot/dts/overlays/BB-SPIDEV1-00A0.dts new file mode 100644 index 0000000000000..a832abd38d05f --- /dev/null +++ b/arch/arm/boot/dts/overlays/BB-SPIDEV1-00A0.dts @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 CircuitCo + * Virtual cape for SPI1 on connector pins P9.29 P9.31 P9.30 P9.28 + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-SPIDEV1-00A0.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P9_28_pinmux { status = "disabled"; }; /* P9_28 (C12) mcasp0_ahclkr.spi1_cs0 */ + P9_30_pinmux { status = "disabled"; }; /* P9_30 (D12) mcasp0_axr0.spi1_d1 */ + P9_29_pinmux { status = "disabled"; }; /* P9_29 (B13) mcasp0_fsx.spi1_d0 */ + P9_31_pinmux { status = "disabled"; }; /* P9_31 (A13) mcasp0_aclkx.spi1_sclk */ +}; + +&am33xx_pinmux { + bb_spi1_pins: pinmux_bb_spi1_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_MCASP0_ACLKX, PIN_INPUT, MUX_MODE3) /* P9_31 (A13) mcasp0_aclkx.spi1_sclk */ + AM33XX_PADCONF(AM335X_PIN_MCASP0_FSX, PIN_INPUT, MUX_MODE3) /* P9_29 (B13) mcasp0_fsx.spi1_d0 */ + AM33XX_PADCONF(AM335X_PIN_MCASP0_AXR0, PIN_INPUT, MUX_MODE3) /* P9_30 (D12) mcasp0_axr0.spi1_d1 */ + AM33XX_PADCONF(AM335X_PIN_MCASP0_AHCLKR, PIN_INPUT, MUX_MODE3) /* P9_28 (C12) mcasp0_ahclkr.spi1_cs0 */ + >; + }; +}; + +&spi1 { + #address-cells = <1>; + #size-cells = <0>; + + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_spi1_pins>; + + /* + * Select the D0 pin as output and D1 as + * input. The default is D0 as input and + * D1 as output. + */ + //ti,pindir-d0-out-d1-in; + + channel@0 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "rohm,dh2228fv"; + symlink = "bone/spi/1.0"; + + reg = <0>; + spi-max-frequency = <16000000>; + spi-cpha; + }; + + channel@1 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "rohm,dh2228fv"; + symlink = "bone/spi/1.1"; + + reg = <1>; + spi-max-frequency = <16000000>; + }; +}; diff --git a/arch/arm/boot/dts/overlays/BB-UART1-00A0.dts b/arch/arm/boot/dts/overlays/BB-UART1-00A0.dts new file mode 100644 index 0000000000000..422dc879d0f64 --- /dev/null +++ b/arch/arm/boot/dts/overlays/BB-UART1-00A0.dts @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 CircuitCo + * Virtual cape for UART1 on connector pins P9.24 P9.26 + */ + +/dts-v1/; +/plugin/; + +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-UART1-00A0.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P9_24_pinmux { status = "disabled"; }; /* uart1_txd */ + P9_26_pinmux { status = "disabled"; }; /* uart1_rxd */ +}; + +&am33xx_pinmux { + bb_uart1_pins: pinmux_bb_uart1_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_UART1_TXD, PIN_OUTPUT, MUX_MODE0) /* P9_24 uart1_txd.uart1_txd */ + AM33XX_PADCONF(AM335X_PIN_UART1_RXD, PIN_INPUT, MUX_MODE0) /* P9_26 uart1_rxd.uart1_rxd */ + //AM33XX_PADCONF(AM335X_PIN_UART1_RTSN, PIN_OUTPUT, MUX_MODE0) /* P9_19 uart1_rtsn.uart1_rtsn */ + //AM33XX_PADCONF(AM335X_PIN_UART1_CTSN, PIN_INPUT, MUX_MODE0) /* P9_20 uart1_ctsn.uart1_ctsn */ + >; + }; +}; + +&uart1 { + /* sudo agetty 115200 ttyS1 */ + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_uart1_pins>; +}; diff --git a/arch/arm/boot/dts/overlays/BB-UART2-00A0.dts b/arch/arm/boot/dts/overlays/BB-UART2-00A0.dts new file mode 100644 index 0000000000000..9bd563d44efcf --- /dev/null +++ b/arch/arm/boot/dts/overlays/BB-UART2-00A0.dts @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 CircuitCo + * Virtual cape for UART2 on connector pins P9.21 P9.22 + */ + +/dts-v1/; +/plugin/; + +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-UART2-00A0.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P9_21_pinmux { status = "disabled"; }; /* P9_21: spi0_d0.uart2_txd */ + P9_22_pinmux { status = "disabled"; }; /* P9_22: spi0_sclk.uart2_rxd */ +}; + +&am33xx_pinmux { + bb_uart2_pins: pinmux_bb_uart2_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_SPI0_D0, PIN_OUTPUT, MUX_MODE1) /* P9_21 spi0_d0.uart2_txd */ + AM33XX_PADCONF(AM335X_PIN_SPI0_SCLK, PIN_INPUT, MUX_MODE1) /* P9_22 spi0_sclk.uart2_rxd */ + //AM33XX_PADCONF(AM335X_PIN_LCD_DATA9, PIN_OUTPUT, MUX_MODE6) /* P8_38 lcd_data9.uart2_rtsn */ + //AM33XX_PADCONF(AM335X_PIN_LCD_DATA8, PIN_INPUT, MUX_MODE6) /* P8_37 lcd_data8.uart2_ctsn */ + >; + }; +}; + +&uart2 { + /* sudo agetty 115200 ttyS2 */ + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_uart2_pins>; +}; diff --git a/arch/arm/boot/dts/overlays/BB-UART4-00A0.dts b/arch/arm/boot/dts/overlays/BB-UART4-00A0.dts new file mode 100644 index 0000000000000..de8f77e6e5a3b --- /dev/null +++ b/arch/arm/boot/dts/overlays/BB-UART4-00A0.dts @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 CircuitCo + * Virtual cape for UART4 on connector pins P9.13 P9.11 + */ + +/dts-v1/; +/plugin/; + +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-UART4-00A0.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P9_13_pinmux { status = "disabled"; }; /* P9_13: uart4_txd */ + P9_11_pinmux { status = "disabled"; }; /* P9_11: uart4_rxd */ +}; + +&am33xx_pinmux { + bb_uart4_pins: pinmux_bb_uart4_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_WPN, PIN_OUTPUT, MUX_MODE6) /* P9_13 gpmc_wpn.uart4_txd_mux2 */ + AM33XX_PADCONF(AM335X_PIN_GPMC_WAIT0, PIN_INPUT, MUX_MODE6) /* P9_13 gpmc_wait0.uart4_rxd_mux2 */ + >; + }; +}; + +&uart4 { + /* sudo agetty 115200 ttyS4 */ + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_uart4_pins>; +}; diff --git a/arch/arm/boot/dts/overlays/BB-W1-P9.12-00A0.dts b/arch/arm/boot/dts/overlays/BB-W1-P9.12-00A0.dts new file mode 100644 index 0000000000000..8b7277b292410 --- /dev/null +++ b/arch/arm/boot/dts/overlays/BB-W1-P9.12-00A0.dts @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2015 Robert Nelson + * Virtual cape for onewire on connector pin P9.12 + */ + +/dts-v1/; +/plugin/; + +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BB-W1-P9.12-00A0.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P9_12_pinmux { status = "disabled"; }; /* P9_12 (U18) gpmc_be1n.gpio1_28 */ +}; + +&am33xx_pinmux { + bb_dallas_w1_pins: pinmux_bb_dallas_w1_pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_GPMC_BEN1, PIN_INPUT_PULLUP, MUX_MODE7) /* P9_12 (U18) gpmc_be1n.gpio1_28 */ + >; + }; +}; + +&{/} { + onewire { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_dallas_w1_pins>; + + compatible = "w1-gpio"; + gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>; + }; +}; diff --git a/arch/arm/boot/dts/overlays/BBORG_COMMS-00A2.dts b/arch/arm/boot/dts/overlays/BBORG_COMMS-00A2.dts new file mode 100644 index 0000000000000..01b7f537ff8c4 --- /dev/null +++ b/arch/arm/boot/dts/overlays/BBORG_COMMS-00A2.dts @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012,2019 Texas Instruments Incorporated - https://www.ti.com/ + * Copyright (C) 2015 Robert Nelson + * Copyright (C) 2015 Sebastian JegerÃ¥s + */ + +/dts-v1/; +/plugin/; + +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BBORG_COMMS-00A2.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P9_24_pinmux { status = "disabled"; }; /* P9_24: uart1_txd.d_can1_rx */ + P9_26_pinmux { status = "disabled"; }; /* P9_26: uart1_rxd.d_can1_tx */ + P9_13_pinmux { status = "disabled"; }; /* P9_13: gpmc_wpn.uart4_txd_mux2 */ + P9_11_pinmux { status = "disabled"; }; /* P9_11: gpmc_wait0.uart4_rxd_mux2 */ +}; + +&am33xx_pinmux { + bborg_comms_can_pins: pinmux_comms_can_pins { + pinctrl-single,pins = < + 0x184 (PIN_INPUT_PULLUP | MUX_MODE2) /* P9_24: uart1_txd.d_can1_rx */ + 0x180 (PIN_OUTPUT_PULLUP | MUX_MODE2) /* P9_26: uart1_rxd.d_can1_tx */ + >; + }; + + bborg_comms_rs485_pins: pinmux_comms_rs485_pins { + pinctrl-single,pins = < + 0x074 (PIN_OUTPUT | MUX_MODE6) /* P9_13: gpmc_wpn.uart4_txd_mux2 */ + 0x070 (PIN_INPUT | MUX_MODE6) /* P9_11: gpmc_wait0.uart4_rxd_mux2 */ + >; + }; +}; + +&dcan1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&bborg_comms_can_pins>; +}; + +&uart4 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&bborg_comms_rs485_pins>; + //rs485-rts-delay = <0 0>; + //rts-gpio = <&gpio3 19 1>; /* GPIO_ACTIVE_HIGH>; */ + //rs485-rts-active-high; + //linux,rs485-enabled-at-boot-time; +}; diff --git a/arch/arm/boot/dts/overlays/BBORG_FAN-A000.dts b/arch/arm/boot/dts/overlays/BBORG_FAN-A000.dts new file mode 100644 index 0000000000000..ab426ff6f6bda --- /dev/null +++ b/arch/arm/boot/dts/overlays/BBORG_FAN-A000.dts @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020 Robert Nelson + */ + +/dts-v1/; +/plugin/; + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BBORG_FAN-A000.kernel = __TIMESTAMP__; + }; +}; + +/* From dra7.dtsi opp_nom-1000000000 */ +&cpu0_opp_table { + opp_slow-500000000 { + opp-hz = /bits/ 64 <1000000000>; + opp-microvolt = <1060000 850000 1150000>, + <1060000 850000 1150000>; + opp-supported-hw = <0xFF 0x01>; + opp-suspend; + }; +}; diff --git a/arch/arm/boot/dts/overlays/BBORG_RELAY-00A2.dts b/arch/arm/boot/dts/overlays/BBORG_RELAY-00A2.dts new file mode 100644 index 0000000000000..9530fa50fe5c7 --- /dev/null +++ b/arch/arm/boot/dts/overlays/BBORG_RELAY-00A2.dts @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2015 Robert Nelson + * Copyright (C) 2019 Amilcar Lucas + */ + +/dts-v1/; +/plugin/; + +&{/chosen} { + overlays { + BBORG_RELAY-00A2.kernel = __TIMESTAMP__; + }; +}; + +&ocp { + P9_41_pinmux { pinctrl-0 = <&P9_41_gpio_pin>;}; + P9_42_pinmux { pinctrl-0 = <&P9_42_gpio_pin>;}; + P9_30_pinmux { pinctrl-0 = <&P9_30_gpio_pin>;}; + P9_27_pinmux { pinctrl-0 = <&P9_27_gpio_pin>;}; +}; + +// relay1 +&bone_led_P9_41 { + status = "okay"; + label = "relay1"; + default-state = "keep"; +}; + +// relay2 +&bone_led_P9_42 { + status = "okay"; + label = "relay2"; + default-state = "keep"; +}; + +// realy3 +&bone_led_P9_30 { + status = "okay"; + label = "relay3"; + default-state = "keep"; +}; + +// realy4 +&bone_led_P9_27 { + status = "okay"; + label = "relay4"; + default-state = "keep"; +}; diff --git a/arch/arm/boot/dts/overlays/BONE-ADC.dts b/arch/arm/boot/dts/overlays/BONE-ADC.dts new file mode 100644 index 0000000000000..dafd8a26fbe0b --- /dev/null +++ b/arch/arm/boot/dts/overlays/BONE-ADC.dts @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020 Deepak Khatri + * See Cape Interface Spec page for more info on Bone Buses + * https://elinux.org/Beagleboard:BeagleBone_cape_interface_spec + * + * Virtual cape for Bone ADC + */ + +/dts-v1/; +/plugin/; + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + BONE-ADC.kernel = __TIMESTAMP__; + }; +}; + +/* + * See these files for the phandles (&bone_*) and other bone bus nodes + * am335x-bbb-bone-buses.dtsi + */ +&bone_adc { + status = "okay"; +}; diff --git a/arch/arm/boot/dts/overlays/LED_P8_03.dts b/arch/arm/boot/dts/overlays/LED_P8_03.dts new file mode 100644 index 0000000000000..44292e8740e2d --- /dev/null +++ b/arch/arm/boot/dts/overlays/LED_P8_03.dts @@ -0,0 +1,17 @@ +/dts-v1/; +/plugin/; + +&{/chosen} { + overlays { + LED_P8_03.kernel = __TIMESTAMP__; + }; +}; + +&ocp { + P8_03_pinmux { pinctrl-0 = <&P8_03_gpio_pd_pin>; }; +}; + +&bone_led_P8_03 { + label = "P8_03"; + status = "okay"; +}; diff --git a/arch/arm/boot/dts/overlays/LED_P8_04.dts b/arch/arm/boot/dts/overlays/LED_P8_04.dts new file mode 100644 index 0000000000000..21121c74512a4 --- /dev/null +++ b/arch/arm/boot/dts/overlays/LED_P8_04.dts @@ -0,0 +1,17 @@ +/dts-v1/; +/plugin/; + +&{/chosen} { + overlays { + LED_P8_04.kernel = __TIMESTAMP__; + }; +}; + +&ocp { + P8_04_pinmux { pinctrl-0 = <&P8_04_gpio_pd_pin>; }; +}; + +&bone_led_P8_04 { + label = "P8_04"; + status = "okay"; +}; diff --git a/arch/arm/boot/dts/overlays/M-BB-BBG-00A0.dts b/arch/arm/boot/dts/overlays/M-BB-BBG-00A0.dts new file mode 100644 index 0000000000000..aa938ade2f0c2 --- /dev/null +++ b/arch/arm/boot/dts/overlays/M-BB-BBG-00A0.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + M-BB-BBG-00A0.kernel = __TIMESTAMP__; + }; +}; + +&{/} { + model = "TI AM335x BeagleBone Green"; + compatible = "ti,am335x-bone-green", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; +}; diff --git a/arch/arm/boot/dts/overlays/M-BB-BBGG-00A0.dts b/arch/arm/boot/dts/overlays/M-BB-BBGG-00A0.dts new file mode 100644 index 0000000000000..6dc003082c036 --- /dev/null +++ b/arch/arm/boot/dts/overlays/M-BB-BBGG-00A0.dts @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + M-BB-BBGG-00A0.kernel = __TIMESTAMP__; + }; +}; + +&{/} { + model = "SeeedStudio BeagleBone Green Gateway"; +}; diff --git a/arch/arm/boot/dts/overlays/Makefile b/arch/arm/boot/dts/overlays/Makefile new file mode 100644 index 0000000000000..7083ccb01c292 --- /dev/null +++ b/arch/arm/boot/dts/overlays/Makefile @@ -0,0 +1,46 @@ +# Overlays for the BeagleBone platform + +dtbo-$(CONFIG_ARCH_OMAP2PLUS) += \ + AM335X-PRU-UIO-00A0.dtbo \ + AM57XX-PRU-UIO-00A0.dtbo \ + BB-ADC-00A0.dtbo \ + BB-BBBW-WL1835-00A0.dtbo \ + BB-BBGG-WL1835-00A0.dtbo \ + BB-BBGW-WL1835-00A0.dtbo \ + BB-BONE-4D4C-01-00A1.dtbo \ + BB-BONE-4D5R-01-00A1.dtbo \ + BB-BONE-eMMC1-01-00A0.dtbo \ + BB-BONE-LCD4-01-00A1.dtbo \ + BB-BONE-NH7C-01-A0.dtbo \ + BB-CAPE-DISP-CT4-00A0.dtbo \ + BB-HDMI-TDA998x-00A0.dtbo \ + BB-I2C1-MCP7940X-00A0.dtbo \ + BB-I2C1-RTC-DS3231.dtbo \ + BB-I2C1-RTC-PCF8563.dtbo \ + BB-I2C2-BME680.dtbo \ + BB-I2C2-MPU6050.dtbo \ + BB-LCD-ADAFRUIT-24-SPI1-00A0.dtbo \ + BB-NHDMI-TDA998x-00A0.dtbo \ + BB-SPIDEV0-00A0.dtbo \ + BB-SPIDEV1-00A0.dtbo \ + BB-W1-P9.12-00A0.dtbo \ + BB-UART1-00A0.dtbo \ + BB-UART2-00A0.dtbo \ + BB-UART4-00A0.dtbo \ + BBORG_COMMS-00A2.dtbo \ + BBORG_FAN-A000.dtbo \ + BBORG_RELAY-00A2.dtbo \ + BONE-ADC.dtbo \ + LED_P8_03.dtbo \ + LED_P8_04.dtbo \ + M-BB-BBG-00A0.dtbo \ + M-BB-BBGG-00A0.dtbo \ + PB-HACKADAY-2021.dtbo \ + PB-MIKROBUS-0.dtbo \ + PB-MIKROBUS-1.dtbo + +targets += dtbs dtbs_install +targets += $(dtbo-y) + +always-y := $(dtbo-y) +clean-files := *.dtbo diff --git a/arch/arm/boot/dts/overlays/PB-HACKADAY-2021.dts b/arch/arm/boot/dts/overlays/PB-HACKADAY-2021.dts new file mode 100644 index 0000000000000..920af8147dbee --- /dev/null +++ b/arch/arm/boot/dts/overlays/PB-HACKADAY-2021.dts @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021 Robert Nelson + */ + +/dts-v1/; +/plugin/; + +#include +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + PB-HACKADAY-2021.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P1_28_pinmux { status = "disabled"; }; + P1_26_pinmux { status = "disabled"; }; + P2_05_pinmux { status = "disabled"; }; + P2_07_pinmux { status = "disabled"; }; +}; + +&am33xx_pinmux { + i2c2_pins: pinmux-i2c2-pins { + pinctrl-single,pins = < + AM33XX_PADCONF(AM335X_PIN_UART1_RTSN, PIN_INPUT_PULLUP, MUX_MODE3) /* (D17) uart1_rtsn.I2C2_SCL */ + AM33XX_PADCONF(AM335X_PIN_UART1_CTSN, PIN_INPUT_PULLUP, MUX_MODE3) /* (D18) uart1_ctsn.I2C2_SDA */ + >; + }; + + grove_button_uart4_pins: pinmux_grove_button_uart4_pins { + pinctrl-single,pins = < + /* slot UART4 */ + AM33XX_IOPAD(0x0870, PIN_INPUT | MUX_MODE7) /* gpmc_wait0.gpio0_30 */ + AM33XX_IOPAD(0x0874, PIN_INPUT | MUX_MODE7) /* gpmc_wpn.gpio0_31 */ + >; + }; +}; + +&{/} { + gpio_keys { + compatible = "gpio-keys"; + pinctrl-names = "default"; + pinctrl-0 = <&grove_button_uart4_pins>; + + #address-cells = <1>; + #size-cells = <0>; + + grove_button_uart4_0@6 { + debounce_interval = <50>; + linux,code = <0x106>; + label = "grove:usr6"; + gpios = <&gpio0 30 0x0>; + gpio-key,wakeup; + autorepeat; + }; + + grove_button_uart4_1@7 { + debounce_interval = <50>; + linux,code = <0x107>; + label = "grove:usr7"; + gpios = <&gpio0 31 0x1>; + gpio-key,wakeup; + autorepeat; + }; + }; +}; + +&i2c1 { + #address-cells = <1>; + #size-cells = <0>; + + jhd1802@3e { + compatible = "seeed-hd44780"; + reg = <0x3e>; + status = "okay"; + }; + + adxl345@53 { + compatible = "adi,adxl345"; + reg = <0x53>; + status = "okay"; + }; +}; + +&i2c2 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c2_pins>; + + #address-cells = <1>; + #size-cells = <0>; + + sht3x@45 { + compatible = "sensirion,sht3x"; + reg = <0x45>; + status = "okay"; + }; +}; diff --git a/arch/arm/boot/dts/overlays/PB-MIKROBUS-0.dts b/arch/arm/boot/dts/overlays/PB-MIKROBUS-0.dts new file mode 100644 index 0000000000000..b516cf58c5956 --- /dev/null +++ b/arch/arm/boot/dts/overlays/PB-MIKROBUS-0.dts @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2020 Vaishnav M A, BeagleBoard.org Foundation. + */ + +/dts-v1/; +/plugin/; + +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + PB-MIKROBUS-0.kernel = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P2_01_pinmux { status = "disabled"; }; + P2_03_pinmux { status = "disabled"; }; + P2_05_pinmux { status = "disabled"; }; + P2_07_pinmux { status = "disabled"; }; + P2_09_pinmux { status = "disabled"; }; + P2_11_pinmux { status = "disabled"; }; + P1_12_pinmux { status = "disabled"; }; + P1_10_pinmux { status = "disabled"; }; + P1_08_pinmux { status = "disabled"; }; + P1_06_pinmux { status = "disabled"; }; + P1_04_pinmux { status = "disabled"; }; + P1_02_pinmux { status = "disabled"; }; +}; + +&{/} { + aliases { + mikrobus0 = "/mikrobus-0"; + }; + + mikrobus-0 { + compatible = "linux,mikrobus"; + status = "okay"; + pinctrl-names = "default", "pwm_default", "pwm_gpio", + "uart_default", "uart_gpio", "i2c_default", + "i2c_gpio", "spi_default", "spi_gpio"; + pinctrl-0 = < + &P2_03_gpio_input_pin + &P1_04_gpio_pin + &P1_02_gpio_pin + >; + pinctrl-1 = <&P2_01_pwm_pin>; + pinctrl-2 = <&P2_01_gpio_pin>; + pinctrl-3 = < + &P2_05_uart_pin + &P2_07_uart_pin + >; + pinctrl-4 = < + &P2_05_gpio_pin + &P2_07_gpio_pin + >; + pinctrl-5 = < + &P2_09_i2c_pin + &P2_11_i2c_pin + >; + pinctrl-6 = < + &P2_09_gpio_pin + &P2_11_gpio_pin + >; + pinctrl-7 = < + &P1_12_spi_pin + &P1_10_spi_pin + &P1_08_spi_sclk_pin + &P1_06_spi_cs_pin + >; + pinctrl-8 = < + &P1_12_gpio_pin + &P1_10_gpio_pin + &P1_08_gpio_pin + &P1_06_gpio_pin + >; + i2c-adapter = <&i2c1>; + spi-master = <0>; + spi-cs = <0 1>; + uart = <&uart4>; + pwms = <&ehrpwm1 0 500000 0>; + mikrobus-gpios = <&gpio1 18 0> , <&gpio0 23 0>, + <&gpio0 30 0> , <&gpio0 31 0>, + <&gpio0 15 0> , <&gpio0 14 0>, + <&gpio0 4 0> , <&gpio0 3 0>, + <&gpio0 2 0> , <&gpio0 5 0>, + <&gpio2 25 0> , <&gpio2 3 0>; + }; +}; + +&spi0 { + status = "okay"; + channel@0{ status = "disabled"; }; +}; + +&uart4 { + status = "okay"; + force-empty-serdev-controller; +}; diff --git a/arch/arm/boot/dts/overlays/PB-MIKROBUS-1.dts b/arch/arm/boot/dts/overlays/PB-MIKROBUS-1.dts new file mode 100644 index 0000000000000..23236ee7c938f --- /dev/null +++ b/arch/arm/boot/dts/overlays/PB-MIKROBUS-1.dts @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2020 Vaishnav M A, BeagleBoard.org Foundation. + */ + +/dts-v1/; +/plugin/; + +#include +#include + +/* + * Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/ + */ +&{/chosen} { + overlays { + PB-MIKROBUS-1 = __TIMESTAMP__; + }; +}; + +/* + * Free up the pins used by the cape from the pinmux helpers. + */ +&ocp { + P1_36_pinmux { status = "disabled"; }; + P1_34_pinmux { status = "disabled"; }; + P1_32_pinmux { status = "disabled"; }; + P1_30_pinmux { status = "disabled"; }; + P1_28_pinmux { status = "disabled"; }; + P1_26_pinmux { status = "disabled"; }; + P2_25_pinmux { status = "disabled"; }; + P2_27_pinmux { status = "disabled"; }; + P2_29_pinmux { status = "disabled"; }; + P2_31_pinmux { status = "disabled"; }; + P2_33_pinmux { status = "disabled"; }; + P2_35_pinmux { status = "disabled"; }; +}; + +&{/} { + aliases { + mikrobus1 = "/mikrobus-1"; + }; + + mikrobus-1 { + compatible = "linux,mikrobus"; + status = "okay"; + pinctrl-names = "default", "pwm_default", "pwm_gpio", + "uart_default", "uart_gpio", "i2c_default", + "i2c_gpio", "spi_default", "spi_gpio"; + pinctrl-0 = < + &P1_34_gpio_input_pin + &P2_33_gpio_pin + &P2_35_gpio_pin + >; + pinctrl-1 = <&P1_36_pwm_pin>; + pinctrl-2 = <&P1_36_gpio_pin>; + pinctrl-3 = < + &P1_32_uart_pin + &P1_30_uart_pin + >; + pinctrl-4 = < + &P1_32_gpio_pin + &P1_30_gpio_pin + >; + pinctrl-5 = < + &P1_26_i2c_pin + &P1_28_i2c_pin + >; + pinctrl-6 = < + &P1_26_gpio_pin + &P1_28_gpio_pin + >; + pinctrl-7 = < + &P2_25_spi_pin + &P2_27_spi_pin + &P2_29_spi_sclk_pin + &P2_31_spi_cs_pin + >; + pinctrl-8 = < + &P2_25_gpio_pin + &P2_27_gpio_pin + &P2_29_gpio_pin + &P2_31_gpio_pin + >; + i2c-adapter = <&i2c2>; + spi-master = <1>; + spi-cs = <1 2>; + uart = <&uart0>; + pwms = <&ehrpwm0 0 500000 0>; + mikrobus-gpios = <&gpio3 14 0> , <&gpio0 26 0>, + <&gpio1 10 0> , <&gpio1 11 0>, + <&gpio0 13 0> , <&gpio0 12 0>, + <&gpio1 9 0> , <&gpio1 8 0>, + <&gpio0 7 0> , <&gpio0 19 0>, + <&gpio1 13 0> , <&gpio2 22 0>; + }; +}; + +&spi1 { + status = "okay"; + channel@0{ status = "disabled"; }; + channel@1{ status = "disabled"; }; +}; + +&uart0 { + status = "okay"; + force-empty-serdev-controller; +}; diff --git a/include/dt-bindings/board/am335x-bbw-bbb-base.h b/include/dt-bindings/board/am335x-bbw-bbb-base.h new file mode 100644 index 0000000000000..35f6d57ef4923 --- /dev/null +++ b/include/dt-bindings/board/am335x-bbw-bbb-base.h @@ -0,0 +1,103 @@ +/* + * This header provides constants for bbw/bbb pinctrl bindings. + * + * Copyright (C) 2014 Robert Nelson + * + * Numbers Based on: https://github.com/derekmolloy/boneDeviceTree/tree/master/docs + */ + +#ifndef _DT_BINDINGS_BOARD_AM335X_BBW_BBB_BASE_H +#define _DT_BINDINGS_BOARD_AM335X_BBW_BBB_BASE_H + +#define BONE_P8_03 0x018 +#define BONE_P8_04 0x01C + +#define BONE_P8_05 0x008 +#define BONE_P8_06 0x00C +#define BONE_P8_07 0x090 +#define BONE_P8_08 0x094 + +#define BONE_P8_09 0x09C +#define BONE_P8_10 0x098 +#define BONE_P8_11 0x034 +#define BONE_P8_12 0x030 + +#define BONE_P8_13 0x024 +#define BONE_P8_14 0x028 +#define BONE_P8_15 0x03C +#define BONE_P8_16 0x038 + +#define BONE_P8_17 0x02C +#define BONE_P8_18 0x08C +#define BONE_P8_19 0x020 +#define BONE_P8_20 0x084 + +#define BONE_P8_21 0x080 +#define BONE_P8_22 0x014 +#define BONE_P8_23 0x010 +#define BONE_P8_24 0x004 + +#define BONE_P8_25 0x000 +#define BONE_P8_26 0x07C +#define BONE_P8_27 0x0E0 +#define BONE_P8_28 0x0E8 + +#define BONE_P8_29 0x0E4 +#define BONE_P8_30 0x0EC +#define BONE_P8_31 0x0D8 +#define BONE_P8_32 0x0DC + +#define BONE_P8_33 0x0D4 +#define BONE_P8_34 0x0CC +#define BONE_P8_35 0x0D0 +#define BONE_P8_36 0x0C8 + +#define BONE_P8_37 0x0C0 +#define BONE_P8_38 0x0C4 +#define BONE_P8_39 0x0B8 +#define BONE_P8_40 0x0BC + +#define BONE_P8_41 0x0B0 +#define BONE_P8_42 0x0B4 +#define BONE_P8_43 0x0A8 +#define BONE_P8_44 0x0AC + +#define BONE_P8_45 0x0A0 +#define BONE_P8_46 0x0A4 + +#define BONE_P9_11 0x070 +#define BONE_P9_12 0x078 + +#define BONE_P9_13 0x074 +#define BONE_P9_14 0x048 +#define BONE_P9_15 0x040 +#define BONE_P9_16 0x04C + +#define BONE_P9_17 0x15C +#define BONE_P9_18 0x158 +#define BONE_P9_19 0x17C +#define BONE_P9_20 0x178 + +#define BONE_P9_21 0x154 +#define BONE_P9_22 0x150 +#define BONE_P9_23 0x044 +#define BONE_P9_24 0x184 + +#define BONE_P9_25 0x1AC +#define BONE_P9_26 0x180 +#define BONE_P9_27 0x1A4 +#define BONE_P9_28 0x19C + +#define BONE_P9_29 0x194 +#define BONE_P9_30 0x198 +#define BONE_P9_31 0x190 + +/* Shared P21 of P11 */ +#define BONE_P9_41A 0x1B4 +#define BONE_P9_41B 0x1A8 + +/* Shared P22 of P11 */ +#define BONE_P9_42A 0x164 +#define BONE_P9_42B 0x1A0 + +#endif diff --git a/include/dt-bindings/board/am335x-bone-pins.h b/include/dt-bindings/board/am335x-bone-pins.h new file mode 100644 index 0000000000000..54e92664ba793 --- /dev/null +++ b/include/dt-bindings/board/am335x-bone-pins.h @@ -0,0 +1,253 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020 Deepak Khatri + * See Cape Interface Spec page for more info on Bone Buses + * https://elinux.org/Beagleboard:BeagleBone_cape_interface_spec + */ + +#ifndef _DT_BINDINGS_BOARD_AM335X_BONE_PINS_H +#define _DT_BINDINGS_BOARD_AM335X_BONE_PINS_H + +#define bb_device 0 +#define board_soc AM335X + +#define gpio_P8_03 &gpio1 6 +#define gpio_P8_04 &gpio1 7 +#define gpio_P8_05 &gpio1 2 +#define gpio_P8_06 &gpio1 3 +#define gpio_P8_07 &gpio2 2 +#define gpio_P8_08 &gpio2 3 +#define gpio_P8_09 &gpio2 5 +#define gpio_P8_10 &gpio2 4 +#define gpio_P8_11 &gpio1 13 +#define gpio_P8_12 &gpio1 12 +#define gpio_P8_13 &gpio0 23 +#define gpio_P8_14 &gpio0 26 +#define gpio_P8_15 &gpio1 15 +#define gpio_P8_16 &gpio1 14 +#define gpio_P8_17 &gpio0 27 +#define gpio_P8_18 &gpio2 1 +#define gpio_P8_19 &gpio0 22 +#define gpio_P8_20 &gpio1 31 +#define gpio_P8_21 &gpio1 30 +#define gpio_P8_22 &gpio1 5 +#define gpio_P8_23 &gpio1 4 +#define gpio_P8_24 &gpio1 1 +#define gpio_P8_25 &gpio1 0 +#define gpio_P8_26 &gpio1 29 +#define gpio_P8_27 &gpio2 22 +#define gpio_P8_28 &gpio2 24 +#define gpio_P8_29 &gpio2 23 +#define gpio_P8_30 &gpio2 25 +#define gpio_P8_31 &gpio0 10 +#define gpio_P8_32 &gpio0 11 +#define gpio_P8_33 &gpio0 9 +#define gpio_P8_34 &gpio2 17 +#define gpio_P8_35 &gpio0 8 +#define gpio_P8_36 &gpio2 16 +#define gpio_P8_37 &gpio2 14 +#define gpio_P8_38 &gpio2 15 +#define gpio_P8_39 &gpio2 12 +#define gpio_P8_40 &gpio2 13 +#define gpio_P8_41 &gpio2 10 +#define gpio_P8_42 &gpio2 11 +#define gpio_P8_43 &gpio2 8 +#define gpio_P8_44 &gpio2 9 +#define gpio_P8_45 &gpio2 6 +#define gpio_P8_46 &gpio2 7 +#define gpio_P9_11 &gpio0 30 +#define gpio_P9_12 &gpio1 28 +#define gpio_P9_13 &gpio0 31 +#define gpio_P9_14 &gpio1 18 +#define gpio_P9_15 &gpio1 16 +#define gpio_P9_16 &gpio1 19 +#define gpio_P9_17 &gpio0 5 +#define gpio_P9_18 &gpio0 4 +#define gpio_P9_19 &gpio0 13 +#define gpio_P9_20 &gpio0 12 +#define gpio_P9_21 &gpio0 3 +#define gpio_P9_22 &gpio0 2 +#define gpio_P9_23 &gpio1 17 +#define gpio_P9_24 &gpio0 15 +#define gpio_P9_25 &gpio3 21 +#define gpio_P9_26 &gpio0 14 +#define gpio_P9_27 &gpio3 19 +#define gpio_P9_28 &gpio3 17 +#define gpio_P9_29 &gpio3 15 +#define gpio_P9_30 &gpio3 16 +#define gpio_P9_31 &gpio3 14 +#define gpio_P9_41 &gpio0 20 +#define gpio_P9_41A &gpio0 20 +#define gpio_P9_41B &gpio3 20 +#define gpio_P9_91 &gpio3 20 +#define gpio_P9_42 &gpio0 7 +#define gpio_P9_42A &gpio0 7 +#define gpio_P9_42B &gpio3 18 +#define gpio_P9_92 &gpio3 18 +#define gpio_A15 &gpio0 19 + +#define P8_03(mode) AM33XX_IOPAD(0x0818, mode) /* R9: gpmc_ad6 */ +#define P8_04(mode) AM33XX_IOPAD(0x081c, mode) /* T9: gpmc_ad7 */ +#define P8_05(mode) AM33XX_IOPAD(0x0808, mode) /* R8: gpmc_ad2 */ +#define P8_06(mode) AM33XX_IOPAD(0x080c, mode) /* T8: gpmc_ad3 */ +#define P8_07(mode) AM33XX_IOPAD(0x0890, mode) /* R7: gpmc_advn_ale */ +#define P8_08(mode) AM33XX_IOPAD(0x0894, mode) /* T7: gpmc_oen_ren */ +#define P8_09(mode) AM33XX_IOPAD(0x089c, mode) /* T6: gpmc_be0n_cle */ +#define P8_10(mode) AM33XX_IOPAD(0x0898, mode) /* U6: gpmc_wen */ +#define P8_11(mode) AM33XX_IOPAD(0x0834, mode) /* R12: gpmc_ad13 */ +#define P8_12(mode) AM33XX_IOPAD(0x0830, mode) /* T12: gpmc_ad12 */ +#define P8_13(mode) AM33XX_IOPAD(0x0824, mode) /* T10: gpmc_ad9 */ +#define P8_14(mode) AM33XX_IOPAD(0x0828, mode) /* T11: gpmc_ad10 */ +#define P8_15(mode) AM33XX_IOPAD(0x083c, mode) /* U13: gpmc_ad15 */ +#define P8_16(mode) AM33XX_IOPAD(0x0838, mode) /* V13: gpmc_ad14 */ +#define P8_17(mode) AM33XX_IOPAD(0x082c, mode) /* U12: gpmc_ad11 */ +#define P8_18(mode) AM33XX_IOPAD(0x088c, mode) /* V12: gpmc_clk */ +#define P8_19(mode) AM33XX_IOPAD(0x0820, mode) /* U10: gpmc_ad8 */ +#define P8_20(mode) AM33XX_IOPAD(0x0884, mode) /* V9: gpmc_csn2 */ +#define P8_21(mode) AM33XX_IOPAD(0x0880, mode) /* U9: gpmc_csn1 */ +#define P8_22(mode) AM33XX_IOPAD(0x0814, mode) /* V8: gpmc_ad5 */ +#define P8_23(mode) AM33XX_IOPAD(0x0810, mode) /* U8: gpmc_ad4 */ +#define P8_24(mode) AM33XX_IOPAD(0x0804, mode) /* V7: gpmc_ad1 */ +#define P8_25(mode) AM33XX_IOPAD(0x0800, mode) /* U7: gpmc_ad0 */ +#define P8_26(mode) AM33XX_IOPAD(0x087c, mode) /* V6: gpmc_csn0 */ +#define P8_27(mode) AM33XX_IOPAD(0x08e0, mode) /* U5: lcd_vsync */ +#define P8_28(mode) AM33XX_IOPAD(0x08e8, mode) /* V5: lcd_pclk */ +#define P8_29(mode) AM33XX_IOPAD(0x08e4, mode) /* R5: lcd_hsync */ +#define P8_30(mode) AM33XX_IOPAD(0x08ec, mode) /* R6: lcd_ac_bias_en */ +#define P8_31(mode) AM33XX_IOPAD(0x08d8, mode) /* V4: lcd_data14 */ +#define P8_32(mode) AM33XX_IOPAD(0x08dc, mode) /* T5: lcd_data15 */ +#define P8_33(mode) AM33XX_IOPAD(0x08d4, mode) /* V3: lcd_data13 */ +#define P8_34(mode) AM33XX_IOPAD(0x08cc, mode) /* U4: lcd_data11 */ +#define P8_35(mode) AM33XX_IOPAD(0x08d0, mode) /* V2: lcd_data12 */ +#define P8_36(mode) AM33XX_IOPAD(0x08c8, mode) /* U3: lcd_data10 */ +#define P8_37(mode) AM33XX_IOPAD(0x08c0, mode) /* U1: lcd_data8 */ +#define P8_38(mode) AM33XX_IOPAD(0x08c4, mode) /* U2: lcd_data9 */ +#define P8_39(mode) AM33XX_IOPAD(0x08b8, mode) /* T3: lcd_data6 */ +#define P8_40(mode) AM33XX_IOPAD(0x08bc, mode) /* T4: lcd_data7 */ +#define P8_41(mode) AM33XX_IOPAD(0x08b0, mode) /* T1: lcd_data4 */ +#define P8_42(mode) AM33XX_IOPAD(0x08b4, mode) /* T2: lcd_data5 */ +#define P8_43(mode) AM33XX_IOPAD(0x08a8, mode) /* R3: lcd_data2 */ +#define P8_44(mode) AM33XX_IOPAD(0x08ac, mode) /* R4: lcd_data3 */ +#define P8_45(mode) AM33XX_IOPAD(0x08a0, mode) /* R1: lcd_data0 */ +#define P8_46(mode) AM33XX_IOPAD(0x08a4, mode) /* R2: lcd_data1 */ +#define P9_11(mode) AM33XX_IOPAD(0x0870, mode) /* T17: gpmc_wait0 */ +#define P9_12(mode) AM33XX_IOPAD(0x0878, mode) /* U18: gpmc_be1n */ +#define P9_13(mode) AM33XX_IOPAD(0x0874, mode) /* U17: gpmc_wpn */ +#define P9_14(mode) AM33XX_IOPAD(0x0848, mode) /* U14: gpmc_a2 */ +#define P9_15(mode) AM33XX_IOPAD(0x0840, mode) /* R13: gpmc_a0 */ +#define P9_16(mode) AM33XX_IOPAD(0x084c, mode) /* T14: gpmc_a3 */ +#define P9_17(mode) AM33XX_IOPAD(0x095c, mode) /* A16: spi0_cs0 */ +#define P9_18(mode) AM33XX_IOPAD(0x0958, mode) /* B16: spi0_d1 */ +#define P9_19(mode) AM33XX_IOPAD(0x097c, mode) /* D17: uart1_rtsn */ +#define P9_20(mode) AM33XX_IOPAD(0x0978, mode) /* D18: uart1_ctsn */ +#define P9_21(mode) AM33XX_IOPAD(0x0954, mode) /* B17: spi0_d0 */ +#define P9_22(mode) AM33XX_IOPAD(0x0950, mode) /* A17: spi0_sclk */ +#define P9_23(mode) AM33XX_IOPAD(0x0844, mode) /* V14: gpmc_a1 */ +#define P9_24(mode) AM33XX_IOPAD(0x0984, mode) /* D15: uart1_txd */ +#define P9_25(mode) AM33XX_IOPAD(0x09ac, mode) /* A14: mcasp0_ahclkx */ +#define P9_26(mode) AM33XX_IOPAD(0x0980, mode) /* D16: uart1_rxd */ +#define P9_27(mode) AM33XX_IOPAD(0x09a4, mode) /* C13: mcasp0_fsr */ +#define P9_28(mode) AM33XX_IOPAD(0x099c, mode) /* C12: mcasp0_ahclkr */ +#define P9_29(mode) AM33XX_IOPAD(0x0994, mode) /* B13: mcasp0_fsx */ +#define P9_30(mode) AM33XX_IOPAD(0x0998, mode) /* D12: mcasp0_axr0 */ +#define P9_31(mode) AM33XX_IOPAD(0x0990, mode) /* A13: mcasp0_aclkx */ +#define P9_41(mode) AM33XX_IOPAD(0x09b4, mode) /* D14: xdma_event_intr1 */ +#define P9_41A(mode) AM33XX_IOPAD(0x09b4, mode) /* D14: xdma_event_intr1 */ +#define P9_41B(mode) AM33XX_IOPAD(0x09a8, mode) /* D13: mcasp0_axr1 */ +#define P9_91(mode) AM33XX_IOPAD(0x09a8, mode) /* D13: mcasp0_axr1 */ +#define P9_42(mode) AM33XX_IOPAD(0x0964, mode) /* C18: P0_in_PWM0_out */ +#define P9_42A(mode) AM33XX_IOPAD(0x0964, mode) /* C18: P0_in_PWM0_out */ +#define P9_42B(mode) AM33XX_IOPAD(0x09a0, mode) /* B12: mcasp0_aclkr */ +#define P9_92(mode) AM33XX_IOPAD(0x09a0, mode) /* B12: mcasp0_aclkr */ + +#define gpio_P1_02 &gpio2 23 +#define gpio_P1_04 &gpio2 25 +#define gpio_P1_06 &gpio0 5 +#define gpio_P1_08 &gpio0 2 +#define gpio_P1_10 &gpio0 3 +#define gpio_P1_12 &gpio0 4 +#define gpio_P1_20 &gpio0 20 +#define gpio_P1_26 &gpio0 12 +#define gpio_P1_28 &gpio0 13 +#define gpio_P1_29 &gpio3 21 +#define gpio_P1_30 &gpio1 11 +#define gpio_P1_31 &gpio3 18 +#define gpio_P1_32 &gpio1 10 +#define gpio_P1_33 &gpio3 15 +#define gpio_P1_34 &gpio0 26 +#define gpio_P1_35 &gpio2 24 +#define gpio_P1_36 &gpio3 14 +#define gpio_P2_01 &gpio1 18 +#define gpio_P2_02 &gpio1 27 +#define gpio_P2_03 &gpio0 23 +#define gpio_P2_04 &gpio1 26 +#define gpio_P2_05 &gpio0 30 +#define gpio_P2_06 &gpio1 25 +#define gpio_P2_07 &gpio0 31 +#define gpio_P2_08 &gpio1 28 +#define gpio_P2_09 &gpio0 15 +#define gpio_P2_10 &gpio1 20 +#define gpio_P2_11 &gpio0 14 +#define gpio_P2_17 &gpio2 1 +#define gpio_P2_18 &gpio1 15 +#define gpio_P2_19 &gpio0 27 +#define gpio_P2_20 &gpio2 0 +#define gpio_P2_22 &gpio1 14 +#define gpio_P2_24 &gpio1 12 +#define gpio_P2_25 &gpio1 9 +#define gpio_P2_27 &gpio1 8 +#define gpio_P2_28 &gpio3 20 +#define gpio_P2_29 &gpio0 7 +#define gpio_P2_30 &gpio3 17 +#define gpio_P2_31 &gpio0 19 +#define gpio_P2_32 &gpio3 16 +#define gpio_P2_33 &gpio1 13 +#define gpio_P2_34 &gpio3 19 +#define gpio_P2_35 &gpio2 22 + +#define P1_02(mode) AM33XX_IOPAD(0x08e4, mode) /* R5: lcd_hsync */ +#define P1_04(mode) AM33XX_IOPAD(0x08ec, mode) /* R6: lcd_ac_bias_en */ +#define P1_06(mode) AM33XX_IOPAD(0x095c, mode) /* A16: spi0_cs0 */ +#define P1_08(mode) AM33XX_IOPAD(0x0950, mode) /* A17: spi0_sclk */ +#define P1_10(mode) AM33XX_IOPAD(0x0954, mode) /* B17: spi0_d0 */ +#define P1_12(mode) AM33XX_IOPAD(0x0958, mode) /* B16: spi0_d1 */ +#define P1_20(mode) AM33XX_IOPAD(0x09b4, mode) /* D14: xdma_event_intr1 */ +#define P1_26(mode) AM33XX_IOPAD(0x0978, mode) /* D18: uart1_ctsn */ +#define P1_28(mode) AM33XX_IOPAD(0x097c, mode) /* D17: uart1_rtsn */ +#define P1_29(mode) AM33XX_IOPAD(0x09ac, mode) /* A14: mcasp0_ahclkx */ +#define P1_30(mode) AM33XX_IOPAD(0x0974, mode) /* E16: uart0_txd */ +#define P1_31(mode) AM33XX_IOPAD(0x09a0, mode) /* B12: mcasp0_aclkr */ +#define P1_32(mode) AM33XX_IOPAD(0x0970, mode) /* E15: uart0_rxd */ +#define P1_33(mode) AM33XX_IOPAD(0x0994, mode) /* B13: mcasp0_fsx */ +#define P1_34(mode) AM33XX_IOPAD(0x0828, mode) /* T11: gpmc_ad10 */ +#define P1_35(mode) AM33XX_IOPAD(0x08e8, mode) /* V5: lcd_pclk */ +#define P1_36(mode) AM33XX_IOPAD(0x0990, mode) /* A13: mcasp0_aclkx */ +#define P2_01(mode) AM33XX_IOPAD(0x0848, mode) /* U14: gpmc_a2 */ +#define P2_02(mode) AM33XX_IOPAD(0x086c, mode) /* V17: gpmc_a11 */ +#define P2_03(mode) AM33XX_IOPAD(0x0824, mode) /* T10: gpmc_ad9 */ +#define P2_04(mode) AM33XX_IOPAD(0x0868, mode) /* T16: gpmc_a10 */ +#define P2_05(mode) AM33XX_IOPAD(0x0870, mode) /* T17: gpmc_wait0 */ +#define P2_06(mode) AM33XX_IOPAD(0x0864, mode) /* U16: gpmc_a9 */ +#define P2_07(mode) AM33XX_IOPAD(0x0874, mode) /* U17: gpmc_wpn */ +#define P2_08(mode) AM33XX_IOPAD(0x0878, mode) /* U18: gpmc_be1n */ +#define P2_09(mode) AM33XX_IOPAD(0x0984, mode) /* D15: uart1_txd */ +#define P2_10(mode) AM33XX_IOPAD(0x0850, mode) /* R14: gpmc_a4 */ +#define P2_11(mode) AM33XX_IOPAD(0x0980, mode) /* D16: uart1_rxd */ +#define P2_17(mode) AM33XX_IOPAD(0x088c, mode) /* V12: gpmc_clk */ +#define P2_18(mode) AM33XX_IOPAD(0x083c, mode) /* U13: gpmc_ad15 */ +#define P2_19(mode) AM33XX_IOPAD(0x082c, mode) /* U12: gpmc_ad11 */ +#define P2_20(mode) AM33XX_IOPAD(0x0888, mode) /* T13: gpmc_csn3 */ +#define P2_22(mode) AM33XX_IOPAD(0x0838, mode) /* V13: gpmc_ad14 */ +#define P2_24(mode) AM33XX_IOPAD(0x0830, mode) /* T12: gpmc_ad12 */ +#define P2_25(mode) AM33XX_IOPAD(0x096c, mode) /* E17: uart0_rtsn */ +#define P2_27(mode) AM33XX_IOPAD(0x0968, mode) /* E18: uart0_ctsn */ +#define P2_28(mode) AM33XX_IOPAD(0x09a8, mode) /* D13: mcasp0_axr1 */ +#define P2_29(mode) AM33XX_IOPAD(0x0964, mode) /* C18: eCAP0_in_PWM0_out */ +#define P2_30(mode) AM33XX_IOPAD(0x099c, mode) /* C12: mcasp0_ahclkr */ +#define P2_31(mode) AM33XX_IOPAD(0x09b0, mode) /* A15: xdma_event_intr0 */ +#define P2_32(mode) AM33XX_IOPAD(0x0998, mode) /* D12: mcasp0_axr0 */ +#define P2_33(mode) AM33XX_IOPAD(0x0834, mode) /* R12: gpmc_ad13 */ +#define P2_34(mode) AM33XX_IOPAD(0x09a4, mode) /* C13: mcasp0_fsr */ +#define P2_35(mode) AM33XX_IOPAD(0x08e0, mode) /* U5: lcd_vsync */ + +#endif diff --git a/include/dt-bindings/board/am572x-bone-pins.h b/include/dt-bindings/board/am572x-bone-pins.h new file mode 100644 index 0000000000000..89f0704909a11 --- /dev/null +++ b/include/dt-bindings/board/am572x-bone-pins.h @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020 Deepak Khatri + * See Cape Interface Spec page for more info on Bone Buses + * https://elinux.org/Beagleboard:BeagleBone_cape_interface_spec + */ + +#ifndef _DT_BINDINGS_BOARD_AM572X_BONE_PINS_H +#define _DT_BINDINGS_BOARD_AM572X_BONE_PINS_H + +#define bb_device 1 +#define board_soc AM572X + +#define gpio_P8_03 &gpio1 24 +#define gpio_P8_04 &gpio1 25 +#define gpio_P8_05 &gpio7 1 +#define gpio_P8_06 &gpio7 2 +#define gpio_P8_07 &gpio6 5 +#define gpio_P8_08 &gpio6 6 +#define gpio_P8_09 &gpio6 18 +#define gpio_P8_10 &gpio6 4 +#define gpio_P8_11 &gpio3 11 +#define gpio_P8_12 &gpio3 10 +#define gpio_P8_13 &gpio4 11 +#define gpio_P8_14 &gpio4 13 +#define gpio_P8_15 &gpio4 3 +#define gpio_P8_16 &gpio4 29 +#define gpio_P8_17 &gpio8 18 +#define gpio_P8_18 &gpio4 9 +#define gpio_P8_19 &gpio4 10 +#define gpio_P8_20 &gpio6 30 +#define gpio_P8_21 &gpio6 29 +#define gpio_P8_22 &gpio1 23 +#define gpio_P8_23 &gpio1 22 +#define gpio_P8_24 &gpio7 0 +#define gpio_P8_25 &gpio6 31 +#define gpio_P8_26 &gpio4 28 +#define gpio_P8_27 &gpio4 23 +#define gpio_P8_28 &gpio4 19 +#define gpio_P8_29 &gpio4 22 +#define gpio_P8_30 &gpio4 20 +#define gpio_P8_31 &gpio8 14 +#define gpio_P8_32 &gpio8 15 +#define gpio_P8_33 &gpio8 13 +#define gpio_P8_34 &gpio8 11 +#define gpio_P8_35 &gpio8 12 +#define gpio_P8_36 &gpio8 10 +#define gpio_P8_37 &gpio8 8 +#define gpio_P8_38 &gpio8 9 +#define gpio_P8_39 &gpio8 6 +#define gpio_P8_40 &gpio8 7 +#define gpio_P8_41 &gpio8 4 +#define gpio_P8_42 &gpio8 5 +#define gpio_P8_43 &gpio8 2 +#define gpio_P8_44 &gpio8 3 +#define gpio_P8_45 &gpio8 0 +#define gpio_P8_46 &gpio8 1 +#define gpio_P9_11 &gpio8 17 +#define gpio_P9_12 &gpio5 0 +#define gpio_P9_13 &gpio6 12 +#define gpio_P9_14 &gpio4 25 +#define gpio_P9_15 &gpio3 12 +#define gpio_P9_16 &gpio4 26 +#define gpio_P9_17 &gpio7 17 +#define gpio_P9_18 &gpio7 16 +#define gpio_P9_19 &gpio7 3 +#define gpio_P9_20 &gpio7 4 +#define gpio_P9_21 &gpio3 3 +#define gpio_P9_22 &gpio6 19 +#define gpio_P9_23 &gpio7 11 +#define gpio_P9_24 &gpio6 15 +#define gpio_P9_25 &gpio6 17 +#define gpio_P9_26 &gpio6 14 +#define gpio_P9_27 &gpio4 15 +#define gpio_P9_28 &gpio4 17 +#define gpio_P9_29 &gpio5 11 +#define gpio_P9_30 &gpio5 12 +#define gpio_P9_31 &gpio5 10 +#define gpio_P9_41 &gpio6 20 +#define gpio_P9_42 &gpio4 18 + +#define P8_03(mode) DRA7XX_CORE_IOPAD(0x379C, mode) /* AB8: mmc3_dat6 */ +#define P8_04(mode) DRA7XX_CORE_IOPAD(0x37A0, mode) /* AB5: mmc3_dat7 */ +#define P8_05(mode) DRA7XX_CORE_IOPAD(0x378C, mode) /* AC9: mmc3_dat2 */ +#define P8_06(mode) DRA7XX_CORE_IOPAD(0x3790, mode) /* AC3: mmc3_dat3 */ +#define P8_07(mode) DRA7XX_CORE_IOPAD(0x36EC, mode) /* G14: mcasp1_axr14 */ +#define P8_08(mode) DRA7XX_CORE_IOPAD(0x36F0, mode) /* F14: mcasp1_axr15 */ +#define P8_09(mode) DRA7XX_CORE_IOPAD(0x3698, mode) /* E17: xref_clk1 */ +#define P8_10(mode) DRA7XX_CORE_IOPAD(0x36E8, mode) /* A13: mcasp1_axr13 */ +#define P8_11(mode) DRA7XX_CORE_IOPAD(0x3510, mode) /* AH4: vin1a_d7 */ +#define P8_12(mode) DRA7XX_CORE_IOPAD(0x350C, mode) /* AG6: vin1a_d6 */ +#define P8_13(mode) DRA7XX_CORE_IOPAD(0x3590, mode) /* D3: vin2a_d10 */ +#define P8_14(mode) DRA7XX_CORE_IOPAD(0x3598, mode) /* D5: vin2a_d12 */ +#define P8_15A(mode) DRA7XX_CORE_IOPAD(0x3570, mode) /* D1: vin2a_d2 */ +#define P8_15B(mode) DRA7XX_CORE_IOPAD(0x35B4, mode) /* A3: vin2a_d19 */ +#define P8_16(mode) DRA7XX_CORE_IOPAD(0x35BC, mode) /* B4: vin2a_d21 */ +#define P8_17(mode) DRA7XX_CORE_IOPAD(0x3624, mode) /* A7: vout1_d18 */ +#define P8_18(mode) DRA7XX_CORE_IOPAD(0x3588, mode) /* F5: vin2a_d8 */ +#define P8_19(mode) DRA7XX_CORE_IOPAD(0x358C, mode) /* E6: vin2a_d9 */ +#define P8_20(mode) DRA7XX_CORE_IOPAD(0x3780, mode) /* AC4: mmc3_cmd */ +#define P8_21(mode) DRA7XX_CORE_IOPAD(0x377C, mode) /* AD4: mmc3_clk */ +#define P8_22(mode) DRA7XX_CORE_IOPAD(0x3798, mode) /* AD6: mmc3_dat5 */ +#define P8_23(mode) DRA7XX_CORE_IOPAD(0x3794, mode) /* AC8: mmc3_dat4 */ +#define P8_24(mode) DRA7XX_CORE_IOPAD(0x3788, mode) /* AC6: mmc3_dat1 */ +#define P8_25(mode) DRA7XX_CORE_IOPAD(0x3784, mode) /* AC7: mmc3_dat0 */ +#define P8_26(mode) DRA7XX_CORE_IOPAD(0x35B8, mode) /* B3: vin2a_d20 */ +#define P8_27A(mode) DRA7XX_CORE_IOPAD(0x35D8, mode) /* E11: vout1_vsync */ +#define P8_27B(mode) DRA7XX_CORE_IOPAD(0x3628, mode) /* A8: vout1_d19 */ +#define P8_28A(mode) DRA7XX_CORE_IOPAD(0x35C8, mode) /* D11: vout1_clk */ +#define P8_28B(mode) DRA7XX_CORE_IOPAD(0x362C, mode) /* C9: vout1_d20 */ +#define P8_29A(mode) DRA7XX_CORE_IOPAD(0x35D4, mode) /* C11: vout1_hsync */ +#define P8_29B(mode) DRA7XX_CORE_IOPAD(0x3630, mode) /* A9: vout1_d21 */ +#define P8_30A(mode) DRA7XX_CORE_IOPAD(0x35CC, mode) /* B10: vout1_de */ +#define P8_30B(mode) DRA7XX_CORE_IOPAD(0x3634, mode) /* B9: vout1_d22 */ +#define P8_31A(mode) DRA7XX_CORE_IOPAD(0x3614, mode) /* C8: vout1_d14 */ +#define P8_31B(mode) DRA7XX_CORE_IOPAD(0x373C, mode) /* G16: mcasp4_axr0 */ +#define P8_32A(mode) DRA7XX_CORE_IOPAD(0x3618, mode) /* C7: vout1_d15 */ +#define P8_32B(mode) DRA7XX_CORE_IOPAD(0x3740, mode) /* D17: mcasp4_axr1 */ +#define P8_33A(mode) DRA7XX_CORE_IOPAD(0x3610, mode) /* C6: vout1_d13 */ +#define P8_33B(mode) DRA7XX_CORE_IOPAD(0x34E8, mode) /* AF9: vin1a_fld0 */ +#define P8_34A(mode) DRA7XX_CORE_IOPAD(0x3608, mode) /* D8: vout1_d11 */ +#define P8_34B(mode) DRA7XX_CORE_IOPAD(0x3564, mode) /* G6: vin2a_vsync0 */ +#define P8_35A(mode) DRA7XX_CORE_IOPAD(0x360C, mode) /* A5: vout1_d12 */ +#define P8_35B(mode) DRA7XX_CORE_IOPAD(0x34E4, mode) /* AD9: vin1a_de0 */ +#define P8_36A(mode) DRA7XX_CORE_IOPAD(0x3604, mode) /* D7: vout1_d10 */ +#define P8_36B(mode) DRA7XX_CORE_IOPAD(0x3568, mode) /* F2: vin2a_d0 */ +#define P8_37A(mode) DRA7XX_CORE_IOPAD(0x35FC, mode) /* E8: vout1_d8 */ +#define P8_37B(mode) DRA7XX_CORE_IOPAD(0x3738, mode) /* A21: mcasp4_fsx */ +#define P8_38A(mode) DRA7XX_CORE_IOPAD(0x3600, mode) /* D9: vout1_d9 */ +#define P8_38B(mode) DRA7XX_CORE_IOPAD(0x3734, mode) /* C18: mcasp4_aclkx */ +#define P8_39(mode) DRA7XX_CORE_IOPAD(0x35F4, mode) /* F8: vout1_d6 */ +#define P8_40(mode) DRA7XX_CORE_IOPAD(0x35F8, mode) /* E7: vout1_d7 */ +#define P8_41(mode) DRA7XX_CORE_IOPAD(0x35EC, mode) /* E9: vout1_d4 */ +#define P8_42(mode) DRA7XX_CORE_IOPAD(0x35F0, mode) /* F9: vout1_d5 */ +#define P8_43(mode) DRA7XX_CORE_IOPAD(0x35E4, mode) /* F10: vout1_d2 */ +#define P8_44(mode) DRA7XX_CORE_IOPAD(0x35E8, mode) /* G11: vout1_d3 */ +#define P8_45A(mode) DRA7XX_CORE_IOPAD(0x35DC, mode) /* F11: vout1_d0 */ +#define P8_45B(mode) DRA7XX_CORE_IOPAD(0x361C, mode) /* B7: vout1_d16 */ +#define P8_46A(mode) DRA7XX_CORE_IOPAD(0x35E0, mode) /* G10: vout1_d1 */ +#define P8_46B(mode) DRA7XX_CORE_IOPAD(0x3638, mode) /* A10: vout1_d23 */ +#define P9_11A(mode) DRA7XX_CORE_IOPAD(0x372C, mode) /* B19: mcasp3_axr0 */ +#define P9_11B(mode) DRA7XX_CORE_IOPAD(0x3620, mode) /* B8: vout1_d17 */ +#define P9_12(mode) DRA7XX_CORE_IOPAD(0x36AC, mode) /* B14: mcasp1_aclkr */ +#define P9_13A(mode) DRA7XX_CORE_IOPAD(0x3730, mode) /* C17: mcasp3_axr1 */ +#define P9_13B(mode) DRA7XX_CORE_IOPAD(0x3680, mode) /* AB10: usb1_drvvbus */ +#define P9_14(mode) DRA7XX_CORE_IOPAD(0x35AC, mode) /* D6: vin2a_d17 */ +#define P9_15(mode) DRA7XX_CORE_IOPAD(0x3514, mode) /* AG4: vin1a_d8 */ +#define P9_16(mode) DRA7XX_CORE_IOPAD(0x35B0, mode) /* C5: vin2a_d18 */ +#define P9_17A(mode) DRA7XX_CORE_IOPAD(0x37CC, mode) /* B24: spi2_cs0 */ +#define P9_17B(mode) DRA7XX_CORE_IOPAD(0x36B8, mode) /* F12: mcasp1_axr1 */ +#define P9_18A(mode) DRA7XX_CORE_IOPAD(0x37C8, mode) /* G17: spi2_d0 */ +#define P9_18B(mode) DRA7XX_CORE_IOPAD(0x36B4, mode) /* G12: mcasp1_axr0 */ +#define P9_19A(mode) DRA7XX_CORE_IOPAD(0x3440, mode) /* R6: gpmc_a0.i2c4_scl */ +#define P9_19B(mode) DRA7XX_CORE_IOPAD(0x357C, mode) /* F4: vin2a_d5.pr1_pru1_gpi2 */ +#define P9_20A(mode) DRA7XX_CORE_IOPAD(0x3444, mode) /* T9: gpmc_a1.i2c4_sda */ +#define P9_20B(mode) DRA7XX_CORE_IOPAD(0x3578, mode) /* D2: vin2a_d4.pr1_pru1_gpi1 */ +#define P9_21A(mode) DRA7XX_CORE_IOPAD(0x34F0, mode) /* AF8: vin1a_vsync0 */ +#define P9_21B(mode) DRA7XX_CORE_IOPAD(0x37C4, mode) /* B22: spi2_d1 */ +#define P9_22A(mode) DRA7XX_CORE_IOPAD(0x369C, mode) /* B26: xref_clk2 */ +#define P9_22B(mode) DRA7XX_CORE_IOPAD(0x37C0, mode) /* A26: spi2_sclk */ +#define P9_23(mode) DRA7XX_CORE_IOPAD(0x37B4, mode) /* A22: spi1_cs1 */ +#define P9_24(mode) DRA7XX_CORE_IOPAD(0x368C, mode) /* F20: gpio6_15 */ +#define P9_25(mode) DRA7XX_CORE_IOPAD(0x3694, mode) /* D18: xref_clk0 */ +#define P9_26A(mode) DRA7XX_CORE_IOPAD(0x3688, mode) /* E21: gpio6_14 */ +#define P9_26B(mode) DRA7XX_CORE_IOPAD(0x3544, mode) /* AE2: vin1a_d20 */ +#define P9_27A(mode) DRA7XX_CORE_IOPAD(0x35A0, mode) /* C3: vin2a_d14 */ +#define P9_27B(mode) DRA7XX_CORE_IOPAD(0x36B0, mode) /* J14: mcasp1_fsr */ +#define P9_28(mode) DRA7XX_CORE_IOPAD(0x36E0, mode) /* A12: mcasp1_axr11 */ +#define P9_29A(mode) DRA7XX_CORE_IOPAD(0x36D8, mode) /* A11: mcasp1_axr9 */ +#define P9_29B(mode) DRA7XX_CORE_IOPAD(0x36A8, mode) /* D14: mcasp1_fsx */ +#define P9_30(mode) DRA7XX_CORE_IOPAD(0x36DC, mode) /* B13: mcasp1_axr10 */ +#define P9_31A(mode) DRA7XX_CORE_IOPAD(0x36D4, mode) /* B12: mcasp1_axr8 */ +#define P9_31B(mode) DRA7XX_CORE_IOPAD(0x36A4, mode) /* C14: mcasp1_aclkx */ +#define P9_41A(mode) DRA7XX_CORE_IOPAD(0x36A0, mode) /* C23: xref_clk3 */ +#define P9_41B(mode) DRA7XX_CORE_IOPAD(0x3580, mode) /* C1: vin2a_d6 */ +#define P9_42A(mode) DRA7XX_CORE_IOPAD(0x36E4, mode) /* E14: mcasp1_axr12 */ +#define P9_42B(mode) DRA7XX_CORE_IOPAD(0x359C, mode) /* C2: vin2a_d13 */ + +#endif diff --git a/include/dt-bindings/board/k3-j721e-bone-pins.h b/include/dt-bindings/board/k3-j721e-bone-pins.h new file mode 100644 index 0000000000000..12d772347911f --- /dev/null +++ b/include/dt-bindings/board/k3-j721e-bone-pins.h @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020 Deepak Khatri + * Copyright (C) 2021 Jason Kridner + * See Cape Interface Spec page for more info on Bone Buses + * https://elinux.org/Beagleboard:BeagleBone_cape_interface_spec + */ + +#ifndef _DT_BINDINGS_BOARD_TDA4VM_BONE_PINS_H +#define _DT_BINDINGS_BOARD_TDA4VM_BONE_PINS_H + +#define bb_device 1 +#define board_soc TDA4VM + +#define gpio_P8_03 &main_gpio0 20 /* AH21: PRG1_PRU0_GPO19 AH21_MCAN6_TX */ +#define gpio_P8_04 &main_gpio0 48 /* AC29: PRG0_PRU0_GPO5 AC29_SYS_BOOTMODE2 */ +#define gpio_P8_05 &main_gpio0 33 /* AH25: PRG1_PRU1_GPO12 AH25_MCAN7_TX */ +#define gpio_P8_06 &main_gpio0 34 /* AG25: PRG1_PRU1_GPO13 AG25_MCAN7_RX */ +#define gpio_P8_07 &main_gpio0 15 /* AD24: PRG1_PRU0_GPO14 AD24_MCAN5_RX */ +#define gpio_P8_08 &main_gpio0 14 /* AG24: PRG1_PRU0_GPO13 AG24_MCAN5_TX */ +#define gpio_P8_09 &main_gpio0 17 /* AE24: PRG1_PRU0_GPO16 AE24_MCAN6_RX */ +#define gpio_P8_10 &main_gpio0 16 /* AC24: PRG1_PRU0_GPO15 AC24_MCAN6_TX */ +#define gpio_P8_11 &main_gpio0 60 /* AB24: PRG0_PRU0_GPO17 AB24_SYS_BOOTMODE7 */ +#define gpio_P8_12 &main_gpio0 59 /* AH28: PRG0_PRU0_GPO16 AH28_PRG0_PWM0_A2 */ +#define gpio_P8_13 &main_gpio0 89 /* V27: RGMII5_TD1 V27_EHRPWM0_B */ +#define gpio_P8_14 &main_gpio0 75 /* AF27: PRG0_PRU1_GPO12 AF27_PRG0_PWM1_A0 */ +#define gpio_P8_15 &main_gpio0 61 /* AB29: PRG0_PRU0_GPO18 AB29_PRG0_ECAP0_IN_APWM_OUT */ +#define gpio_P8_16 &main_gpio0 62 /* AB28: PRG0_PRU0_GPO19 AB28_PRG0_PWM0_TZ_OUT */ +#define gpio_P8_17 &main_gpio0 3 /* AF22: PRG1_PRU0_GPO2 AF22_PRG1_PWM2_A0 */ +#define gpio_P8_18 &main_gpio0 4 /* AJ23: PRG1_PRU0_GPO3 AJ23_PRG1_PWM3_A2 */ +#define gpio_P8_19 &main_gpio0 88 /* V29: RGMII5_TD2 V29_EHRPWM0_A */ +#define gpio_P8_20 &main_gpio0 76 /* AF26: PRG0_PRU1_GPO13 AF26_PRG0_PWM1_B0 */ +#define gpio_P8_21 &main_gpio0 30 /* AF21: PRG1_PRU1_GPO9 AF21_MCAN8_TX */ +#define gpio_P8_22 &main_gpio0 5 /* AH23: PRG1_PRU0_GPO4 AH23_UART2_RXD */ +#define gpio_P8_23 &main_gpio0 31 /* AB23: PRG1_PRU1_GPO10 AB23_MCAN8_RX */ +#define gpio_P8_24 &main_gpio0 6 /* AD20: PRG1_PRU0_GPO5 AD20_SYS_BOOTMODE0 */ +#define gpio_P8_25 &main_gpio0 35 /* AH26: PRG1_PRU1_GPO14 AH26_PRG1_PRU1_GPO14 */ +#define gpio_P8_26 &main_gpio0 51 /* AC27: PRG0_PRU0_GPO8 AC27_PRG0_PWM2_A1 */ +#define gpio_P8_27 &main_gpio0 71 /* AA28: PRG0_PRU1_GPO8 AA28_PRG0_PRU1_GPO8 */ +#define gpio_P8_28 &main_gpio0 72 /* Y24: PRG0_PRU1_GPO9 Y24_PRG0_UART0_RXD */ +#define gpio_P8_29 &main_gpio0 73 /* AA25: PRG0_PRU1_GPO10 AA25_PRG0_UART0_TXD */ +#define gpio_P8_30 &main_gpio0 74 /* AG26: PRG0_PRU1_GPO11 AG26_PRG0_PRU1_GPO11 */ +#define gpio_P8_31 gpio_P8_31A +#define gpio_P8_31A &main_gpio0 32 /* AJ25: PRG1_PRU1_GPO11 AJ25_AE29 */ +#define gpio_P8_31B &main_gpio0 63 /* AE29: PRG0_PRU1_GPO0 AJ25_AE29 */ +#define gpio_P8_32 gpio_P8_32A +#define gpio_P8_32A &main_gpio0 26 /* AG21: PRG1_PRU1_GPO5 AG21_AD28 */ +#define gpio_P8_32B &main_gpio0 64 /* AD28: PRG0_PRU1_GPO1 AG21_AD28 */ +#define gpio_P8_33 gpio_P8_33A +#define gpio_P8_33A &main_gpio0 25 /* AH24: PRG1_PRU1_GPO4 AH24_AA2 */ +#define gpio_P8_33B &main_gpio0 111 /* AA2: SPI0_CS0 AH24_AA2 */ +#define gpio_P8_34 &main_gpio0 7 /* AD22: PRG1_PRU0_GPO6 AD22_UART2_TXD */ +#define gpio_P8_35 gpio_P8_35A +#define gpio_P8_35A &main_gpio0 24 /* AD23: PRG1_PRU1_GPO3 AD23_Y3 */ +#define gpio_P8_35B &main_gpio0 116 /* Y3: SPI1_CS0 AD23_Y3 */ +#define gpio_P8_36 &main_gpio0 8 /* AE20: PRG1_PRU0_GPO7 AE20_MCAN4_TX */ +#define gpio_P8_37 gpio_P8_37A +#define gpio_P8_37A &main_gpio0 106 /* Y27: RGMII6_RD2 Y27_AD21 */ +#define gpio_P8_37B &main_gpio0 11 /* AD21: PRG1_PRU0_GPO10 Y27_AD21 */ +#define gpio_P8_38 gpio_P8_38A +#define gpio_P8_38A &main_gpio0 9 /* AJ20: PRG1_PRU0_GPO8 Y29_AJ20 */ +#define gpio_P8_38B &main_gpio0 105 /* Y29: RGMII6_RD3 Y29_AJ20 */ +#define gpio_P8_39 &main_gpio0 69 /* AC26: PRG0_PRU1_GPO6 AC26_PRG0_PRU1_GPO6 */ +#define gpio_P8_40 &main_gpio0 70 /* AA24: PRG0_PRU1_GPO7 AA24_PRG0_PRU1_GPO7 */ +#define gpio_P8_41 &main_gpio0 67 /* AD29: PRG0_PRU1_GPO4 AD29_PRG0_PRU1_GPO4 */ +#define gpio_P8_42 &main_gpio0 68 /* AB27: PRG0_PRU1_GPO5 AB27_SYS_BOOTMODE6 */ +#define gpio_P8_43 &main_gpio0 65 /* AD27: PRG0_PRU1_GPO2 AD27_PRG0_PRU1_GPO2 */ +#define gpio_P8_44 &main_gpio0 66 /* AC25: PRG0_PRU1_GPO3 AC25_PRG0_PRU1_GPO3 */ +#define gpio_P8_45 &main_gpio0 79 /* AG29: PRG0_PRU1_GPO16 AG29_PRG0_PRU1_GPO16 */ +#define gpio_P8_46 &main_gpio0 80 /* Y25: PRG0_PRU1_GPO17 Y25_SYS_BOOTMODE3 */ +#define gpio_P9_11 &main_gpio0 1 /* AC23: PRG1_PRU0_GPO0 AC23_UART0_RXD */ +#define gpio_P9_12 &main_gpio0 45 /* AE27: PRG0_PRU0_GPO2 AE27_MCASP0_ACLKR */ +#define gpio_P9_13 &main_gpio0 2 /* AG22: PRG1_PRU0_GPO1 AG22_UART0_TXD */ +#define gpio_P9_14 &main_gpio0 93 /* U27: RGMII5_RD3 U27_EHRPWM2_A */ +#define gpio_P9_15 &main_gpio0 47 /* AD25: PRG0_PRU0_GPO4 AD25_PRG0_PRU0_GPO4 */ +#define gpio_P9_16 &main_gpio0 94 /* U24: RGMII5_RD2 U24_EHRPWM2_B */ +#define gpio_P9_17 gpio_P9_17A +#define gpio_P9_17A &main_gpio0 28 /* AC21: PRG1_PRU1_GPO7 AC21_AA3 */ +#define gpio_P9_17B &main_gpio0 115 /* AA3: SPI0_D1 AC21_AA3 */ +#define gpio_P9_18 gpio_P9_18A +#define gpio_P9_18A &main_gpio0 40 /* AH22: PRG1_PRU1_GPO19 AH22_Y2 */ +#define gpio_P9_18B &main_gpio0 120 /* Y2: SPI1_D1 AH22_Y2 */ +#define gpio_P9_19 gpio_P9_19A +#define gpio_P9_19A &main_gpio1 1 /* W5: MCAN0_RX W5_AF29 */ +#define gpio_P9_19B &main_gpio0 78 /* AF29: PRG0_PRU1_GPO15 W5_AF29 */ +#define gpio_P9_20 gpio_P9_20A +#define gpio_P9_20A &main_gpio1 2 /* W6: MCAN0_TX W6_AE25 */ +#define gpio_P9_20B &main_gpio0 77 /* AE25: PRG0_PRU1_GPO14 W6_AE25 */ +#define gpio_P9_21 gpio_P9_21A +#define gpio_P9_21A &main_gpio0 39 /* AJ22: PRG1_PRU1_GPO18 AJ22_U28 */ +#define gpio_P9_21B &main_gpio0 90 /* U28: RGMII5_TD0 AJ22_U28 */ +#define gpio_P9_22 gpio_P9_22A +#define gpio_P9_22A &main_gpio0 38 /* AC22: PRG1_PRU1_GPO17 AC22_U29 */ +#define gpio_P9_22B &main_gpio0 91 /* U29: RGMII5_TXC AC22_U29 */ +#define gpio_P9_23 &main_gpio0 10 /* AG20: PRG1_PRU0_GPO9 AG20_SPI6_CS1 */ +#define gpio_P9_24 gpio_P9_24A +#define gpio_P9_24A &main_gpio0 119 /* Y5: SPI1_D0 Y5_AJ24 */ +#define gpio_P9_24B &main_gpio0 13 /* AJ24: PRG1_PRU0_GPO12 Y5_AJ24 */ +#define gpio_P9_25 gpio_P9_25A +#define gpio_P9_25A &main_gpio0 127 /* AC4: UART1_CTSn AC4_W26 */ +#define gpio_P9_25B &main_gpio0 104 /* W26: RGMII6_RXC AC4_W26 */ +#define gpio_P9_26 gpio_P9_26A +#define gpio_P9_26A &main_gpio0 118 /* Y1: SPI1_CLK Y1_AF24 */ +#define gpio_P9_26B &main_gpio0 12 /* AF24: PRG1_PRU0_GPO11 Y1_AF24 */ +#define gpio_P9_27 gpio_P9_27A +#define gpio_P9_27A &main_gpio0 46 /* AD26: PRG0_PRU0_GPO3 AD26_AB1 */ +#define gpio_P9_27B &main_gpio0 124 /* AB1: UART0_RTSn AD26_AB1 */ +#define gpio_P9_28 gpio_P9_28A +#define gpio_P9_28A &main_gpio1 11 /* U2: ECAP0_IN_APWM_OUT U2_AF28 */ +#define gpio_P9_28B &main_gpio0 43 /* AF28: PRG0_PRU0_GPO0 U2_AF28 */ +#define gpio_P9_29 gpio_P9_29A +#define gpio_P9_29A &main_gpio1 14 /* V5: TIMER_IO1 V5_AB25 */ +#define gpio_P9_29B &main_gpio0 53 /* AB25: PRG0_PRU0_GPO10 V5_AB25 */ +#define gpio_P9_30 gpio_P9_30A +#define gpio_P9_30A &main_gpio1 13 /* V6: TIMER_IO0 V6_AE28 */ +#define gpio_P9_30B &main_gpio0 44 /* AE28: PRG0_PRU0_GPO1 V6_AE28 */ +#define gpio_P9_31 gpio_P9_31A +#define gpio_P9_31A &main_gpio1 12 /* U3: EXT_REFCLK1 U3_AB26 */ +#define gpio_P9_31B &main_gpio0 52 /* AB26: PRG0_PRU0_GPO9 U3_AB26 */ +#define gpio_P9_33 &main_gpio0 50 +#define gpio_P9_33B &main_gpio0 50 /* AC28: PRG0_PRU0_GPO7 K24_AC28 */ +#define gpio_P9_35 &main_gpio0 55 +#define gpio_P9_35B &main_gpio0 55 /* AH27: PRG0_PRU0_GPO12 K29_AH27 */ +#define gpio_P9_36 &main_gpio0 56 +#define gpio_P9_36B &main_gpio0 56 /* AH29: PRG0_PRU0_GPO13 K27_AH29 */ +#define gpio_P9_37 &main_gpio0 57 +#define gpio_P9_37B &main_gpio0 57 /* AG28: PRG0_PRU0_GPO14 K28_AG28 */ +#define gpio_P9_38 &main_gpio0 58 +#define gpio_P9_38B &main_gpio0 58 /* AG27: PRG0_PRU0_GPO15 L28_AG27 */ +#define gpio_P9_39 &main_gpio0 54 +#define gpio_P9_39B &main_gpio0 54 /* AJ28: PRG0_PRU0_GPO11 K25_AJ28 */ +#define gpio_P9_40 &main_gpio0 81 +#define gpio_P9_40B &main_gpio0 81 /* AA26: PRG0_PRU1_GPO18 K26_AA26 */ +#define gpio_P9_41 &main_gpio1 0 /* AD5: UART1_RTSn AD5_EQEP0_I */ +#define gpio_P9_42 gpio_P9_42A +#define gpio_P9_42A &main_gpio0 123 /* AC2: UART0_CTSn AC2_AJ21 */ +#define gpio_P9_42B &main_gpio0 18 /* AJ21: PRG1_PRU0_GPO17 AC2_AJ21 */ + +#define P8_03(mode, mux) J721E_IOPAD(0x54, mode, mux) /* AH21: PRG1_PRU0_GPO19 AH21_MCAN6_TX */ +#define P8_04(mode, mux) J721E_IOPAD(0xC4, mode, mux) /* AC29: PRG0_PRU0_GPO5 AC29_SYS_BOOTMODE2 */ +#define P8_05(mode, mux) J721E_IOPAD(0x88, mode, mux) /* AH25: PRG1_PRU1_GPO12 AH25_MCAN7_TX */ +#define P8_06(mode, mux) J721E_IOPAD(0x8C, mode, mux) /* AG25: PRG1_PRU1_GPO13 AG25_MCAN7_RX */ +#define P8_07(mode, mux) J721E_IOPAD(0x3C, mode, mux) /* AD24: PRG1_PRU0_GPO14 AD24_MCAN5_RX */ +#define P8_08(mode, mux) J721E_IOPAD(0x38, mode, mux) /* AG24: PRG1_PRU0_GPO13 AG24_MCAN5_TX */ +#define P8_09(mode, mux) J721E_IOPAD(0x44, mode, mux) /* AE24: PRG1_PRU0_GPO16 AE24_MCAN6_RX */ +#define P8_10(mode, mux) J721E_IOPAD(0x40, mode, mux) /* AC24: PRG1_PRU0_GPO15 AC24_MCAN6_TX */ +#define P8_11(mode, mux) J721E_IOPAD(0xF4, mode, mux) /* AB24: PRG0_PRU0_GPO17 AB24_SYS_BOOTMODE7 */ +#define P8_12(mode, mux) J721E_IOPAD(0xF0, mode, mux) /* AH28: PRG0_PRU0_GPO16 AH28_PRG0_PWM0_A2 */ +#define P8_13(mode, mux) J721E_IOPAD(0x168, mode, mux) /* V27: RGMII5_TD1 V27_EHRPWM0_B */ +#define P8_14(mode, mux) J721E_IOPAD(0x130, mode, mux) /* AF27: PRG0_PRU1_GPO12 AF27_PRG0_PWM1_A0 */ +#define P8_15(mode, mux) J721E_IOPAD(0xF8, mode, mux) /* AB29: PRG0_PRU0_GPO18 AB29_PRG0_ECAP0_IN_APWM_OUT */ +#define P8_16(mode, mux) J721E_IOPAD(0xFC, mode, mux) /* AB28: PRG0_PRU0_GPO19 AB28_PRG0_PWM0_TZ_OUT */ +#define P8_17(mode, mux) J721E_IOPAD(0xC, mode, mux) /* AF22: PRG1_PRU0_GPO2 AF22_PRG1_PWM2_A0 */ +#define P8_18(mode, mux) J721E_IOPAD(0x10, mode, mux) /* AJ23: PRG1_PRU0_GPO3 AJ23_PRG1_PWM3_A2 */ +#define P8_19(mode, mux) J721E_IOPAD(0x164, mode, mux) /* V29: RGMII5_TD2 V29_EHRPWM0_A */ +#define P8_20(mode, mux) J721E_IOPAD(0x134, mode, mux) /* AF26: PRG0_PRU1_GPO13 AF26_PRG0_PWM1_B0 */ +#define P8_21(mode, mux) J721E_IOPAD(0x7C, mode, mux) /* AF21: PRG1_PRU1_GPO9 AF21_MCAN8_TX */ +#define P8_22(mode, mux) J721E_IOPAD(0x14, mode, mux) /* AH23: PRG1_PRU0_GPO4 AH23_UART2_RXD */ +#define P8_23(mode, mux) J721E_IOPAD(0x80, mode, mux) /* AB23: PRG1_PRU1_GPO10 AB23_MCAN8_RX */ +#define P8_24(mode, mux) J721E_IOPAD(0x18, mode, mux) /* AD20: PRG1_PRU0_GPO5 AD20_SYS_BOOTMODE0 */ +#define P8_25(mode, mux) J721E_IOPAD(0x90, mode, mux) /* AH26: PRG1_PRU1_GPO14 AH26_PRG1_PRU1_GPO14 */ +#define P8_26(mode, mux) J721E_IOPAD(0xD0, mode, mux) /* AC27: PRG0_PRU0_GPO8 AC27_PRG0_PWM2_A1 */ +#define P8_27(mode, mux) J721E_IOPAD(0x120, mode, mux) /* AA28: PRG0_PRU1_GPO8 AA28_PRG0_PRU1_GPO8 */ +#define P8_28(mode, mux) J721E_IOPAD(0x124, mode, mux) /* Y24: PRG0_PRU1_GPO9 Y24_PRG0_UART0_RXD */ +#define P8_29(mode, mux) J721E_IOPAD(0x128, mode, mux) /* AA25: PRG0_PRU1_GPO10 AA25_PRG0_UART0_TXD */ +#define P8_30(mode, mux) J721E_IOPAD(0x12C, mode, mux) /* AG26: PRG0_PRU1_GPO11 AG26_PRG0_PRU1_GPO11 */ +#define P8_31A(mode, mux) J721E_IOPAD(0x84, mode, mux) /* AJ25: PRG1_PRU1_GPO11 AJ25_AE29 */ +#define P8_31B(mode, mux) J721E_IOPAD(0x100, mode, mux) /* AE29: PRG0_PRU1_GPO0 AJ25_AE29 */ +#define P8_32A(mode, mux) J721E_IOPAD(0x6C, mode, mux) /* AG21: PRG1_PRU1_GPO5 AG21_AD28 */ +#define P8_32B(mode, mux) J721E_IOPAD(0x104, mode, mux) /* AD28: PRG0_PRU1_GPO1 AG21_AD28 */ +#define P8_33A(mode, mux) J721E_IOPAD(0x68, mode, mux) /* AH24: PRG1_PRU1_GPO4 AH24_AA2 */ +#define P8_33B(mode, mux) J721E_IOPAD(0x1C0, mode, mux) /* AA2: SPI0_CS0 AH24_AA2 */ +#define P8_34(mode, mux) J721E_IOPAD(0x1C, mode, mux) /* AD22: PRG1_PRU0_GPO6 AD22_UART2_TXD */ +#define P8_35A(mode, mux) J721E_IOPAD(0x64, mode, mux) /* AD23: PRG1_PRU1_GPO3 AD23_Y3 */ +#define P8_35B(mode, mux) J721E_IOPAD(0x1D4, mode, mux) /* Y3: SPI1_CS0 AD23_Y3 */ +#define P8_36(mode, mux) J721E_IOPAD(0x20, mode, mux) /* AE20: PRG1_PRU0_GPO7 AE20_MCAN4_TX */ +#define P8_37A(mode, mux) J721E_IOPAD(0x1AC, mode, mux) /* Y27: RGMII6_RD2 Y27_AD21 */ +#define P8_37B(mode, mux) J721E_IOPAD(0x2C, mode, mux) /* AD21: PRG1_PRU0_GPO10 Y27_AD21 */ +#define P8_38A(mode, mux) J721E_IOPAD(0x24, mode, mux) /* AJ20: PRG1_PRU0_GPO8 Y29_AJ20 */ +#define P8_38B(mode, mux) J721E_IOPAD(0x1A8, mode, mux) /* Y29: RGMII6_RD3 Y29_AJ20 */ +#define P8_39(mode, mux) J721E_IOPAD(0x118, mode, mux) /* AC26: PRG0_PRU1_GPO6 AC26_PRG0_PRU1_GPO6 */ +#define P8_40(mode, mux) J721E_IOPAD(0x11C, mode, mux) /* AA24: PRG0_PRU1_GPO7 AA24_PRG0_PRU1_GPO7 */ +#define P8_41(mode, mux) J721E_IOPAD(0x110, mode, mux) /* AD29: PRG0_PRU1_GPO4 AD29_PRG0_PRU1_GPO4 */ +#define P8_42(mode, mux) J721E_IOPAD(0x114, mode, mux) /* AB27: PRG0_PRU1_GPO5 AB27_SYS_BOOTMODE6 */ +#define P8_43(mode, mux) J721E_IOPAD(0x108, mode, mux) /* AD27: PRG0_PRU1_GPO2 AD27_PRG0_PRU1_GPO2 */ +#define P8_44(mode, mux) J721E_IOPAD(0x10C, mode, mux) /* AC25: PRG0_PRU1_GPO3 AC25_PRG0_PRU1_GPO3 */ +#define P8_45(mode, mux) J721E_IOPAD(0x140, mode, mux) /* AG29: PRG0_PRU1_GPO16 AG29_PRG0_PRU1_GPO16 */ +#define P8_46(mode, mux) J721E_IOPAD(0x144, mode, mux) /* Y25: PRG0_PRU1_GPO17 Y25_SYS_BOOTMODE3 */ +#define P9_11(mode, mux) J721E_IOPAD(0x4, mode, mux) /* AC23: PRG1_PRU0_GPO0 AC23_UART0_RXD */ +#define P9_12(mode, mux) J721E_IOPAD(0xB8, mode, mux) /* AE27: PRG0_PRU0_GPO2 AE27_MCASP0_ACLKR */ +#define P9_13(mode, mux) J721E_IOPAD(0x8, mode, mux) /* AG22: PRG1_PRU0_GPO1 AG22_UART0_TXD */ +#define P9_14(mode, mux) J721E_IOPAD(0x178, mode, mux) /* U27: RGMII5_RD3 U27_EHRPWM2_A */ +#define P9_15(mode, mux) J721E_IOPAD(0xC0, mode, mux) /* AD25: PRG0_PRU0_GPO4 AD25_PRG0_PRU0_GPO4 */ +#define P9_16(mode, mux) J721E_IOPAD(0x17C, mode, mux) /* U24: RGMII5_RD2 U24_EHRPWM2_B */ +#define P9_17A(mode, mux) J721E_IOPAD(0x74, mode, mux) /* AC21: PRG1_PRU1_GPO7 AC21_AA3 */ +#define P9_17B(mode, mux) J721E_IOPAD(0x1D0, mode, mux) /* AA3: SPI0_D1 AC21_AA3 */ +#define P9_18A(mode, mux) J721E_IOPAD(0xA4, mode, mux) /* AH22: PRG1_PRU1_GPO19 AH22_Y2 */ +#define P9_18B(mode, mux) J721E_IOPAD(0x1E4, mode, mux) /* Y2: SPI1_D1 AH22_Y2 */ +#define P9_19A(mode, mux) J721E_IOPAD(0x208, mode, mux) /* W5: MCAN0_RX W5_AF29 */ +#define P9_19B(mode, mux) J721E_IOPAD(0x13C, mode, mux) /* AF29: PRG0_PRU1_GPO15 W5_AF29 */ +#define P9_20A(mode, mux) J721E_IOPAD(0x20C, mode, mux) /* W6: MCAN0_TX W6_AE25 */ +#define P9_20B(mode, mux) J721E_IOPAD(0x138, mode, mux) /* AE25: PRG0_PRU1_GPO14 W6_AE25 */ +#define P9_21A(mode, mux) J721E_IOPAD(0xA0, mode, mux) /* AJ22: PRG1_PRU1_GPO18 AJ22_U28 */ +#define P9_21B(mode, mux) J721E_IOPAD(0x16C, mode, mux) /* U28: RGMII5_TD0 AJ22_U28 */ +#define P9_22A(mode, mux) J721E_IOPAD(0x9C, mode, mux) /* AC22: PRG1_PRU1_GPO17 AC22_U29 */ +#define P9_22B(mode, mux) J721E_IOPAD(0x170, mode, mux) /* U29: RGMII5_TXC AC22_U29 */ +#define P9_23(mode, mux) J721E_IOPAD(0x28, mode, mux) /* AG20: PRG1_PRU0_GPO9 AG20_SPI6_CS1 */ +#define P9_24A(mode, mux) J721E_IOPAD(0x1E0, mode, mux) /* Y5: SPI1_D0 Y5_AJ24 */ +#define P9_24B(mode, mux) J721E_IOPAD(0x34, mode, mux) /* AJ24: PRG1_PRU0_GPO12 Y5_AJ24 */ +#define P9_25A(mode, mux) J721E_IOPAD(0x200, mode, mux) /* AC4: UART1_CTSn AC4_W26 */ +#define P9_25B(mode, mux) J721E_IOPAD(0x1A4, mode, mux) /* W26: RGMII6_RXC AC4_W26 */ +#define P9_26A(mode, mux) J721E_IOPAD(0x1DC, mode, mux) /* Y1: SPI1_CLK Y1_AF24 */ +#define P9_26B(mode, mux) J721E_IOPAD(0x30, mode, mux) /* AF24: PRG1_PRU0_GPO11 Y1_AF24 */ +#define P9_27A(mode, mux) J721E_IOPAD(0xBC, mode, mux) /* AD26: PRG0_PRU0_GPO3 AD26_AB1 */ +#define P9_27B(mode, mux) J721E_IOPAD(0x1F4, mode, mux) /* AB1: UART0_RTSn AD26_AB1 */ +#define P9_28A(mode, mux) J721E_IOPAD(0x230, mode, mux) /* U2: ECAP0_IN_APWM_OUT U2_AF28 */ +#define P9_28B(mode, mux) J721E_IOPAD(0xB0, mode, mux) /* AF28: PRG0_PRU0_GPO0 U2_AF28 */ +#define P9_29A(mode, mux) J721E_IOPAD(0x23C, mode, mux) /* V5: TIMER_IO1 V5_AB25 */ +#define P9_29B(mode, mux) J721E_IOPAD(0xD8, mode, mux) /* AB25: PRG0_PRU0_GPO10 V5_AB25 */ +#define P9_30A(mode, mux) J721E_IOPAD(0x238, mode, mux) /* V6: TIMER_IO0 V6_AE28 */ +#define P9_30B(mode, mux) J721E_IOPAD(0xB4, mode, mux) /* AE28: PRG0_PRU0_GPO1 V6_AE28 */ +#define P9_31A(mode, mux) J721E_IOPAD(0x234, mode, mux) /* U3: EXT_REFCLK1 U3_AB26 */ +#define P9_31B(mode, mux) J721E_IOPAD(0xD4, mode, mux) /* AB26: PRG0_PRU0_GPO9 U3_AB26 */ +#define P9_33A(mode, mux) J721E_WKUP_IOPAD(0x140, mode, mux) /* K24: MCU_ADC0_AIN4 K24_AC28 */ +#define P9_33B(mode, mux) J721E_IOPAD(0xCC, mode, mux) /* AC28: PRG0_PRU0_GPO7 K24_AC28 */ +#define P9_35A(mode, mux) J721E_WKUP_IOPAD(0x148, mode, mux) /* K29: MCU_ADC0_AIN6 K29_AH27 */ +#define P9_35B(mode, mux) J721E_IOPAD(0xE0, mode, mux) /* AH27: PRG0_PRU0_GPO12 K29_AH27 */ +#define P9_36A(mode, mux) J721E_WKUP_IOPAD(0x144, mode, mux) /* K27: MCU_ADC0_AIN5 K27_AH29 */ +#define P9_36B(mode, mux) J721E_IOPAD(0xE4, mode, mux) /* AH29: PRG0_PRU0_GPO13 K27_AH29 */ +#define P9_37A(mode, mux) J721E_WKUP_IOPAD(0x138, mode, mux) /* K28: MCU_ADC0_AIN2 K28_AG28 */ +#define P9_37B(mode, mux) J721E_IOPAD(0xE8, mode, mux) /* AG28: PRG0_PRU0_GPO14 K28_AG28 */ +#define P9_38A(mode, mux) J721E_WKUP_IOPAD(0x13C, mode, mux) /* L28: MCU_ADC0_AIN3 L28_AG27 */ +#define P9_38B(mode, mux) J721E_IOPAD(0xEC, mode, mux) /* AG27: PRG0_PRU0_GPO15 L28_AG27 */ +#define P9_39A(mode, mux) J721E_WKUP_IOPAD(0x130, mode, mux) /* K25: MCU_ADC0_AIN0 K25_AJ28 */ +#define P9_39B(mode, mux) J721E_IOPAD(0xDC, mode, mux) /* AJ28: PRG0_PRU0_GPO11 K25_AJ28 */ +#define P9_40A(mode, mux) J721E_WKUP_IOPAD(0x134, mode, mux) /* K26: MCU_ADC0_AIN1 K26_AA26 */ +#define P9_40B(mode, mux) J721E_IOPAD(0x148, mode, mux) /* AA26: PRG0_PRU1_GPO18 K26_AA26 */ +#define P9_41(mode, mux) J721E_IOPAD(0x204, mode, mux) /* AD5: UART1_RTSn AD5_EQEP0_I */ +#define P9_42A(mode, mux) J721E_IOPAD(0x1F0, mode, mux) /* AC2: UART0_CTSn AC2_AJ21 */ +#define P9_42B(mode, mux) J721E_IOPAD(0x4C, mode, mux) /* AJ21: PRG1_PRU0_GPO17 AC2_AJ21 */ + +#endif diff --git a/include/dt-bindings/clock/dra7.h b/include/dt-bindings/clock/dra7.h index 5ec4137231e30..7d57063b8a651 100644 --- a/include/dt-bindings/clock/dra7.h +++ b/include/dt-bindings/clock/dra7.h @@ -84,6 +84,10 @@ #define DRA7_L3_MAIN_2_CLKCTRL DRA7_CLKCTRL_INDEX(0x20) #define DRA7_L3_INSTR_CLKCTRL DRA7_CLKCTRL_INDEX(0x28) +/* iva clocks */ +#define DRA7_IVA_CLKCTRL DRA7_CLKCTRL_INDEX(0x20) +#define DRA7_SL2IF_CLKCTRL DRA7_CLKCTRL_INDEX(0x28) + /* dss clocks */ #define DRA7_DSS_CORE_CLKCTRL DRA7_CLKCTRL_INDEX(0x20) #define DRA7_BB2D_CLKCTRL DRA7_CLKCTRL_INDEX(0x30) diff --git a/include/dt-bindings/clock/omap5.h b/include/dt-bindings/clock/omap5.h index 41775272fd275..90e0d4b00127d 100644 --- a/include/dt-bindings/clock/omap5.h +++ b/include/dt-bindings/clock/omap5.h @@ -32,6 +32,8 @@ /* l3main2 clocks */ #define OMAP5_L3_MAIN_2_CLKCTRL OMAP5_CLKCTRL_INDEX(0x20) +#define OMAP5_L3_MAIN_2_GPMC_CLKCTRL OMAP5_CLKCTRL_INDEX(0x28) +#define OMAP5_L3_MAIN_2_OCMC_RAM_CLKCTRL OMAP5_CLKCTRL_INDEX(0x30) /* ipu clocks */ #define OMAP5_MMU_IPU_CLKCTRL OMAP5_CLKCTRL_INDEX(0x20) diff --git a/include/dt-bindings/pinctrl/omap.h b/include/dt-bindings/pinctrl/omap.h index f48245ff87e5f..6257180424132 100644 --- a/include/dt-bindings/pinctrl/omap.h +++ b/include/dt-bindings/pinctrl/omap.h @@ -64,8 +64,8 @@ #define OMAP3_WKUP_IOPAD(pa, val) OMAP_IOPAD_OFFSET((pa), 0x2a00) (val) #define DM814X_IOPAD(pa, val) OMAP_IOPAD_OFFSET((pa), 0x0800) (val) #define DM816X_IOPAD(pa, val) OMAP_IOPAD_OFFSET((pa), 0x0800) (val) -#define AM33XX_IOPAD(pa, val) OMAP_IOPAD_OFFSET((pa), 0x0800) (val) (0) -#define AM33XX_PADCONF(pa, conf, mux) OMAP_IOPAD_OFFSET((pa), 0x0800) (conf) (mux) +#define AM33XX_IOPAD(pa, val) OMAP_IOPAD_OFFSET((pa), 0x0800) (val) +#define AM33XX_PADCONF(pa, dir, mux) OMAP_IOPAD_OFFSET((pa), 0x0800) ((dir) | (mux)) /* * Macros to allow using the offset from the padconf physical address From 7d598e953d02cdc19771f0a9654030aef09cf46d Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Fri, 3 Feb 2023 19:24:50 -0600 Subject: [PATCH 16/65] backports: wlcore: from: linux.git Reference: v5.13.19 Signed-off-by: Robert Nelson --- drivers/net/wireless/ti/wl1251/cmd.c | 38 ++++++++---------------- drivers/net/wireless/ti/wl1251/debugfs.c | 2 +- drivers/net/wireless/ti/wlcore/main.c | 6 ++-- drivers/net/wireless/ti/wlcore/spi.c | 3 +- drivers/net/wireless/ti/wlcore/sysfs.c | 2 +- 5 files changed, 19 insertions(+), 32 deletions(-) diff --git a/drivers/net/wireless/ti/wl1251/cmd.c b/drivers/net/wireless/ti/wl1251/cmd.c index ea0215246c5c8..d7a869106782f 100644 --- a/drivers/net/wireless/ti/wl1251/cmd.c +++ b/drivers/net/wireless/ti/wl1251/cmd.c @@ -63,7 +63,7 @@ int wl1251_cmd_send(struct wl1251 *wl, u16 id, void *buf, size_t len) * * @wl: wl struct * @buf: buffer containing the command, with all headers, must work with dma - * @len: length of the buffer + * @buf_len: length of the buffer * @answer: is answer needed */ int wl1251_cmd_test(struct wl1251 *wl, void *buf, size_t buf_len, u8 answer) @@ -175,10 +175,8 @@ int wl1251_cmd_vbm(struct wl1251 *wl, u8 identity, wl1251_debug(DEBUG_CMD, "cmd vbm"); vbm = kzalloc(sizeof(*vbm), GFP_KERNEL); - if (!vbm) { - ret = -ENOMEM; - goto out; - } + if (!vbm) + return -ENOMEM; /* Count and period will be filled by the target */ vbm->tim.bitmap_ctrl = bitmap_control; @@ -213,10 +211,8 @@ int wl1251_cmd_data_path_rx(struct wl1251 *wl, u8 channel, bool enable) wl1251_debug(DEBUG_CMD, "cmd data path"); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (!cmd) { - ret = -ENOMEM; - goto out; - } + if (!cmd) + return -ENOMEM; cmd->channel = channel; @@ -279,10 +275,8 @@ int wl1251_cmd_join(struct wl1251 *wl, u8 bss_type, u8 channel, u8 *bssid; join = kzalloc(sizeof(*join), GFP_KERNEL); - if (!join) { - ret = -ENOMEM; - goto out; - } + if (!join) + return -ENOMEM; wl1251_debug(DEBUG_CMD, "cmd join%s ch %d %d/%d", bss_type == BSS_TYPE_IBSS ? " ibss" : "", @@ -324,10 +318,8 @@ int wl1251_cmd_ps_mode(struct wl1251 *wl, u8 ps_mode) wl1251_debug(DEBUG_CMD, "cmd set ps mode"); ps_params = kzalloc(sizeof(*ps_params), GFP_KERNEL); - if (!ps_params) { - ret = -ENOMEM; - goto out; - } + if (!ps_params) + return -ENOMEM; ps_params->ps_mode = ps_mode; ps_params->send_null_data = 1; @@ -356,10 +348,8 @@ int wl1251_cmd_read_memory(struct wl1251 *wl, u32 addr, void *answer, wl1251_debug(DEBUG_CMD, "cmd read memory"); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (!cmd) { - ret = -ENOMEM; - goto out; - } + if (!cmd) + return -ENOMEM; WARN_ON(len > MAX_READ_SIZE); len = min_t(size_t, len, MAX_READ_SIZE); @@ -401,10 +391,8 @@ int wl1251_cmd_template_set(struct wl1251 *wl, u16 cmd_id, cmd_len = ALIGN(sizeof(*cmd) + buf_len, 4); cmd = kzalloc(cmd_len, GFP_KERNEL); - if (!cmd) { - ret = -ENOMEM; - goto out; - } + if (!cmd) + return -ENOMEM; cmd->size = cpu_to_le16(buf_len); diff --git a/drivers/net/wireless/ti/wl1251/debugfs.c b/drivers/net/wireless/ti/wl1251/debugfs.c index d48746e640cca..a1b778a0fda00 100644 --- a/drivers/net/wireless/ti/wl1251/debugfs.c +++ b/drivers/net/wireless/ti/wl1251/debugfs.c @@ -39,7 +39,7 @@ static const struct file_operations name## _ops = { \ #define DEBUGFS_ADD(name, parent) \ wl->debugfs.name = debugfs_create_file(#name, 0400, parent, \ - wl, &name## _ops); \ + wl, &name## _ops) \ #define DEBUGFS_DEL(name) \ do { \ diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 6e402d62dbe4a..8509b989940c2 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -2227,7 +2227,7 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif) switch (ieee80211_vif_type_p2p(vif)) { case NL80211_IFTYPE_P2P_CLIENT: wlvif->p2p = 1; - /* fall-through */ + fallthrough; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_DEVICE: wlvif->bss_type = BSS_TYPE_STA_BSS; @@ -2237,7 +2237,7 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif) break; case NL80211_IFTYPE_P2P_GO: wlvif->p2p = 1; - /* fall-through */ + fallthrough; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_MESH_POINT: wlvif->bss_type = BSS_TYPE_AP_BSS; @@ -5381,7 +5381,7 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw, if (wl->ba_rx_session_count >= wl->ba_rx_session_count_max) { ret = -EBUSY; - wl1271_error("exceeded max RX BA sessions"); + wl1271_debug(DEBUG_RX, "exceeded max RX BA sessions"); break; } diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c index 18c4d998ce4b9..f26fc150ecd01 100644 --- a/drivers/net/wireless/ti/wlcore/spi.c +++ b/drivers/net/wireless/ti/wlcore/spi.c @@ -391,7 +391,7 @@ static int wl12xx_spi_set_power(struct device *child, bool enable) return ret; } -/** +/* * wl12xx_spi_set_block_size * * This function is not needed for spi mode, but need to be present. @@ -431,7 +431,6 @@ MODULE_DEVICE_TABLE(of, wlcore_spi_of_match_table); /** * wlcore_probe_of - DT node parsing. * @spi: SPI slave device parameters. - * @res: resource parameters. * @glue: wl12xx SPI bus to slave device glue parameters. * @pdev_data: wlcore device parameters */ diff --git a/drivers/net/wireless/ti/wlcore/sysfs.c b/drivers/net/wireless/ti/wlcore/sysfs.c index 7ac1814182baf..5cf0379b88b6c 100644 --- a/drivers/net/wireless/ti/wlcore/sysfs.c +++ b/drivers/net/wireless/ti/wlcore/sysfs.c @@ -100,7 +100,7 @@ static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t count) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct wl1271 *wl = dev_get_drvdata(dev); ssize_t len; int ret; From 8055a721486b084eb8ecee99c6635d32bb955e2f Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Fri, 3 Feb 2023 19:25:37 -0600 Subject: [PATCH 17/65] backports: spidev: from: linux.git Reference: v5.13.19 Signed-off-by: Robert Nelson --- drivers/spi/spidev.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 859910ec8d9f6..f56e0e975a469 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -682,6 +682,8 @@ static const struct of_device_id spidev_dt_ids[] = { { .compatible = "lwn,bk4" }, { .compatible = "dh,dhcom-board" }, { .compatible = "menlo,m53cpld" }, + { .compatible = "cisco,spi-petra" }, + { .compatible = "micron,spi-authenta" }, {}, }; MODULE_DEVICE_TABLE(of, spidev_dt_ids); From a63f3eeaf16c6ae8fbcbaf335501bb978a173d93 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Sat, 4 Feb 2023 16:15:17 -0600 Subject: [PATCH 18/65] backports: spi: from: linux.git Reference: v5.10.162 Signed-off-by: Robert Nelson --- drivers/spi/spi-omap2-mcspi.c | 156 +++++++++++++++++++++++----------- 1 file changed, 106 insertions(+), 50 deletions(-) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index fe4723e96cacc..3596bbe4b7760 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -91,6 +91,10 @@ struct omap2_mcspi_dma { struct dma_chan *dma_tx; struct dma_chan *dma_rx; + + struct completion dma_tx_completion; + struct completion dma_rx_completion; + char dma_rx_ch_name[14]; char dma_tx_ch_name[14]; }; @@ -112,7 +116,7 @@ struct omap2_mcspi_regs { }; struct omap2_mcspi { - struct completion txrxdone; + struct completion txdone; struct spi_master *master; /* Virtual base address of the controller */ void __iomem *base; @@ -372,6 +376,30 @@ static int mcspi_wait_for_completion(struct omap2_mcspi *mcspi, return 0; } +static void omap2_mcspi_rx_callback(void *data) +{ + struct spi_device *spi = data; + struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master); + struct omap2_mcspi_dma *mcspi_dma = &mcspi->dma_channels[spi->chip_select]; + + /* We must disable the DMA RX request */ + omap2_mcspi_set_dma_req(spi, 1, 0); + + complete(&mcspi_dma->dma_rx_completion); +} + +static void omap2_mcspi_tx_callback(void *data) +{ + struct spi_device *spi = data; + struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master); + struct omap2_mcspi_dma *mcspi_dma = &mcspi->dma_channels[spi->chip_select]; + + /* We must disable the DMA TX request */ + omap2_mcspi_set_dma_req(spi, 0, 0); + + complete(&mcspi_dma->dma_tx_completion); +} + static void omap2_mcspi_tx_dma(struct spi_device *spi, struct spi_transfer *xfer, struct dma_slave_config cfg) @@ -386,9 +414,12 @@ static void omap2_mcspi_tx_dma(struct spi_device *spi, dmaengine_slave_config(mcspi_dma->dma_tx, &cfg); tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, xfer->tx_sg.sgl, - xfer->tx_sg.nents, DMA_MEM_TO_DEV, DMA_CTRL_ACK); - + xfer->tx_sg.nents, + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (tx) { + tx->callback = omap2_mcspi_tx_callback; + tx->callback_param = spi; dmaengine_submit(tx); } else { /* FIXME: fall back to PIO? */ @@ -414,9 +445,6 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, struct omap2_mcspi_cs *cs = spi->controller_state; void __iomem *chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0; struct dma_async_tx_descriptor *tx; - dma_cookie_t dma_rx_cookie = 0; - struct dma_tx_state mcspi_dma_rxstate; - enum dma_status dma_status; mcspi = spi_master_get_devdata(spi->master); mcspi_dma = &mcspi->dma_channels[spi->chip_select]; @@ -473,10 +501,12 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, } tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, sg_out[0], - out_mapped_nents[0], DMA_DEV_TO_MEM, DMA_CTRL_ACK); - + out_mapped_nents[0], DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (tx) { - dma_rx_cookie = dmaengine_submit(tx); + tx->callback = omap2_mcspi_rx_callback; + tx->callback_param = spi; + dmaengine_submit(tx); } else { /* FIXME: fall back to PIO? */ } @@ -484,20 +514,10 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, dma_async_issue_pending(mcspi_dma->dma_rx); omap2_mcspi_set_dma_req(spi, 1, 1); - ret = mcspi_wait_for_completion(mcspi, &mcspi->txrxdone); - - /* - * Before disabling RX DMA we need to confirm whether DMA RX is complete. - * This polling completes on the first attempt itself in most cases. - */ - do { - dma_status = dmaengine_tx_status(mcspi_dma->dma_rx, dma_rx_cookie, - &mcspi_dma_rxstate); - } while (dma_status != DMA_COMPLETE); - - omap2_mcspi_set_dma_req(spi, 1, 0); + ret = mcspi_wait_for_completion(mcspi, &mcspi_dma->dma_rx_completion); if (ret || mcspi->slave_aborted) { dmaengine_terminate_sync(mcspi_dma->dma_rx); + omap2_mcspi_set_dma_req(spi, 1, 0); return 0; } @@ -568,8 +588,8 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) enum dma_slave_buswidth width; unsigned es; void __iomem *chstat_reg; + void __iomem *irqstat_reg; int wait_res; - int ret; mcspi = spi_master_get_devdata(spi->master); mcspi_dma = &mcspi->dma_channels[spi->chip_select]; @@ -599,36 +619,68 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) tx = xfer->tx_buf; mcspi->slave_aborted = false; - reinit_completion(&mcspi->txrxdone); - mcspi_write_reg(spi->master, OMAP2_MCSPI_IRQENABLE, OMAP2_MCSPI_IRQSTATUS_EOW); - if (tx) + reinit_completion(&mcspi_dma->dma_tx_completion); + reinit_completion(&mcspi_dma->dma_rx_completion); + reinit_completion(&mcspi->txdone); + if (tx) { + /* Enable EOW IRQ to know end of tx in slave mode */ + if (spi_controller_is_slave(spi->master)) + mcspi_write_reg(spi->master, + OMAP2_MCSPI_IRQENABLE, + OMAP2_MCSPI_IRQSTATUS_EOW); omap2_mcspi_tx_dma(spi, xfer, cfg); + } - if (rx) + if (rx != NULL) count = omap2_mcspi_rx_dma(spi, xfer, cfg, es); - ret = mcspi_wait_for_completion(mcspi, &mcspi->txrxdone); - omap2_mcspi_set_dma_req(spi, 0, 0); - if (ret || mcspi->slave_aborted) - return 0; + if (tx != NULL) { + int ret; + + ret = mcspi_wait_for_completion(mcspi, &mcspi_dma->dma_tx_completion); + if (ret || mcspi->slave_aborted) { + dmaengine_terminate_sync(mcspi_dma->dma_tx); + omap2_mcspi_set_dma_req(spi, 0, 0); + return 0; + } + + if (spi_controller_is_slave(mcspi->master)) { + ret = mcspi_wait_for_completion(mcspi, &mcspi->txdone); + if (ret || mcspi->slave_aborted) + return 0; + } - /* for TX_ONLY mode, be sure all words have shifted out */ - if (tx && !rx) { - chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0; if (mcspi->fifo_depth > 0) { - wait_res = mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_TXFFE); - if (wait_res < 0) - dev_err(&spi->dev, "TXFFE timed out\n"); - } else { - wait_res = mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_TXS); - if (wait_res < 0) - dev_err(&spi->dev, "TXS timed out\n"); + irqstat_reg = mcspi->base + OMAP2_MCSPI_IRQSTATUS; + + if (mcspi_wait_for_reg_bit(irqstat_reg, + OMAP2_MCSPI_IRQSTATUS_EOW) < 0) + dev_err(&spi->dev, "EOW timed out\n"); + + mcspi_write_reg(mcspi->master, OMAP2_MCSPI_IRQSTATUS, + OMAP2_MCSPI_IRQSTATUS_EOW); } - if (wait_res >= 0 && (mcspi_wait_for_reg_bit(chstat_reg, - OMAP2_MCSPI_CHSTAT_EOT) < 0)) - dev_err(&spi->dev, "EOT timed out\n"); - } + /* for TX_ONLY mode, be sure all words have shifted out */ + if (rx == NULL) { + chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0; + if (mcspi->fifo_depth > 0) { + wait_res = mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_TXFFE); + if (wait_res < 0) + dev_err(&spi->dev, "TXFFE timed out\n"); + } else { + wait_res = mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_TXS); + if (wait_res < 0) + dev_err(&spi->dev, "TXS timed out\n"); + } + if (wait_res >= 0 && + (mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_EOT) < 0)) + dev_err(&spi->dev, "EOT timed out\n"); + } + } return count; } @@ -953,6 +1005,9 @@ static int omap2_mcspi_request_dma(struct omap2_mcspi *mcspi, mcspi_dma->dma_rx = NULL; } + init_completion(&mcspi_dma->dma_rx_completion); + init_completion(&mcspi_dma->dma_tx_completion); + no_dma: return ret; } @@ -1043,10 +1098,8 @@ static irqreturn_t omap2_mcspi_irq_handler(int irq, void *data) /* Disable IRQ and wakeup slave xfer task */ mcspi_write_reg(mcspi->master, OMAP2_MCSPI_IRQENABLE, 0); - if (irqstat & OMAP2_MCSPI_IRQSTATUS_EOW) { - complete_all(&mcspi->txrxdone); - mcspi_write_reg(mcspi->master, OMAP2_MCSPI_IRQSTATUS, OMAP2_MCSPI_IRQSTATUS_EOW); - } + if (irqstat & OMAP2_MCSPI_IRQSTATUS_EOW) + complete(&mcspi->txdone); return IRQ_HANDLED; } @@ -1054,9 +1107,12 @@ static irqreturn_t omap2_mcspi_irq_handler(int irq, void *data) static int omap2_mcspi_slave_abort(struct spi_master *master) { struct omap2_mcspi *mcspi = spi_master_get_devdata(master); + struct omap2_mcspi_dma *mcspi_dma = mcspi->dma_channels; mcspi->slave_aborted = true; - complete_all(&mcspi->txrxdone); + complete(&mcspi_dma->dma_rx_completion); + complete(&mcspi_dma->dma_tx_completion); + complete(&mcspi->txdone); return 0; } @@ -1442,7 +1498,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev) dev_err(&pdev->dev, "no irq resource found\n"); goto free_master; } - init_completion(&mcspi->txrxdone); + init_completion(&mcspi->txdone); status = devm_request_irq(&pdev->dev, status, omap2_mcspi_irq_handler, 0, pdev->name, mcspi); From 9237aa8758f067b0dc5d4b7453e0d528e4add125 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Fri, 3 Feb 2023 19:26:28 -0600 Subject: [PATCH 19/65] backports: iio: from: linux.git Reference: v5.10.166 Signed-off-by: Robert Nelson --- drivers/iio/accel/adis16201.c | 1 + drivers/iio/accel/adis16209.c | 1 + drivers/iio/adc/ad_sigma_delta.c | 8 +-- drivers/iio/adc/ti-adc128s052.c | 14 ++-- drivers/iio/gyro/adis16136.c | 1 + drivers/iio/gyro/adis16260.c | 1 + drivers/iio/imu/adis.c | 98 +++++++++++++++------------ drivers/iio/imu/adis16400.c | 1 + drivers/iio/imu/adis16460.c | 5 +- drivers/iio/imu/adis16475.c | 6 +- drivers/iio/imu/adis16480.c | 1 + drivers/iio/imu/adis_buffer.c | 10 +-- drivers/iio/imu/adis_trigger.c | 20 +++--- drivers/iio/temperature/ltc2983.c | 10 +-- drivers/staging/iio/accel/adis16203.c | 1 + drivers/staging/iio/accel/adis16240.c | 1 + include/linux/iio/imu/adis.h | 63 ++++++++++------- include/linux/interrupt.h | 4 ++ 18 files changed, 144 insertions(+), 102 deletions(-) diff --git a/drivers/iio/accel/adis16201.c b/drivers/iio/accel/adis16201.c index 84bbdfd2f2ba3..b4ae4f86da3e1 100644 --- a/drivers/iio/accel/adis16201.c +++ b/drivers/iio/accel/adis16201.c @@ -304,3 +304,4 @@ MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); MODULE_DESCRIPTION("Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("spi:adis16201"); +MODULE_IMPORT_NS(IIO_ADISLIB); diff --git a/drivers/iio/accel/adis16209.c b/drivers/iio/accel/adis16209.c index 4a841aec6268b..e6e465f397d9d 100644 --- a/drivers/iio/accel/adis16209.c +++ b/drivers/iio/accel/adis16209.c @@ -314,3 +314,4 @@ MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); MODULE_DESCRIPTION("Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("spi:adis16209"); +MODULE_IMPORT_NS(IIO_ADISLIB); diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 3a6f239d4acca..496cb2b26bfda 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -280,10 +280,10 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, unsigned int data_reg; int ret = 0; - if (iio_buffer_enabled(indio_dev)) - return -EBUSY; + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; - mutex_lock(&indio_dev->mlock); ad_sigma_delta_set_channel(sigma_delta, chan->address); spi_bus_lock(sigma_delta->spi->master); @@ -322,7 +322,7 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); sigma_delta->bus_locked = false; spi_bus_unlock(sigma_delta->spi->master); - mutex_unlock(&indio_dev->mlock); + iio_device_release_direct_mode(indio_dev); if (ret) return ret; diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c index 83c1ae07b3e9a..8618ae7bc0671 100644 --- a/drivers/iio/adc/ti-adc128s052.c +++ b/drivers/iio/adc/ti-adc128s052.c @@ -193,13 +193,13 @@ static int adc128_remove(struct spi_device *spi) } static const struct of_device_id adc128_of_match[] = { - { .compatible = "ti,adc128s052", }, - { .compatible = "ti,adc122s021", }, - { .compatible = "ti,adc122s051", }, - { .compatible = "ti,adc122s101", }, - { .compatible = "ti,adc124s021", }, - { .compatible = "ti,adc124s051", }, - { .compatible = "ti,adc124s101", }, + { .compatible = "ti,adc128s052", .data = (void*)0L, }, + { .compatible = "ti,adc122s021", .data = (void*)1L, }, + { .compatible = "ti,adc122s051", .data = (void*)1L, }, + { .compatible = "ti,adc122s101", .data = (void*)1L, }, + { .compatible = "ti,adc124s021", .data = (void*)2L, }, + { .compatible = "ti,adc124s051", .data = (void*)2L, }, + { .compatible = "ti,adc124s101", .data = (void*)2L, }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, adc128_of_match); diff --git a/drivers/iio/gyro/adis16136.c b/drivers/iio/gyro/adis16136.c index a11ae9db0d11b..74db8edb42834 100644 --- a/drivers/iio/gyro/adis16136.c +++ b/drivers/iio/gyro/adis16136.c @@ -599,3 +599,4 @@ module_spi_driver(adis16136_driver); MODULE_AUTHOR("Lars-Peter Clausen "); MODULE_DESCRIPTION("Analog Devices ADIS16133/ADIS16135/ADIS16136 gyroscope driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ADISLIB); diff --git a/drivers/iio/gyro/adis16260.c b/drivers/iio/gyro/adis16260.c index e7c9a3e31c45a..1e45d93de5b7c 100644 --- a/drivers/iio/gyro/adis16260.c +++ b/drivers/iio/gyro/adis16260.c @@ -438,3 +438,4 @@ module_spi_driver(adis16260_driver); MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); MODULE_DESCRIPTION("Analog Devices ADIS16260/5 Digital Gyroscope Sensor"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ADISLIB); diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index 715eef81bc248..e9821814afec1 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -34,8 +34,8 @@ * @value: The value to write to device (up to 4 bytes) * @size: The size of the @value (in bytes) */ -int __adis_write_reg(struct adis *adis, unsigned int reg, - unsigned int value, unsigned int size) +int __adis_write_reg(struct adis *adis, unsigned int reg, unsigned int value, + unsigned int size) { unsigned int page = reg / ADIS_PAGE_SIZE; int ret, i; @@ -118,14 +118,14 @@ int __adis_write_reg(struct adis *adis, unsigned int reg, ret = spi_sync(adis->spi, &msg); if (ret) { dev_err(&adis->spi->dev, "Failed to write register 0x%02X: %d\n", - reg, ret); + reg, ret); } else { adis->current_page = page; } return ret; } -EXPORT_SYMBOL_GPL(__adis_write_reg); +EXPORT_SYMBOL_NS_GPL(__adis_write_reg, IIO_ADISLIB); /** * __adis_read_reg() - read N bytes from register (unlocked version) @@ -134,8 +134,8 @@ EXPORT_SYMBOL_GPL(__adis_write_reg); * @val: The value read back from the device * @size: The size of the @val buffer */ -int __adis_read_reg(struct adis *adis, unsigned int reg, - unsigned int *val, unsigned int size) +int __adis_read_reg(struct adis *adis, unsigned int reg, unsigned int *val, + unsigned int size) { unsigned int page = reg / ADIS_PAGE_SIZE; struct spi_message msg; @@ -205,12 +205,12 @@ int __adis_read_reg(struct adis *adis, unsigned int reg, ret = spi_sync(adis->spi, &msg); if (ret) { dev_err(&adis->spi->dev, "Failed to read register 0x%02X: %d\n", - reg, ret); + reg, ret); return ret; - } else { - adis->current_page = page; } + adis->current_page = page; + switch (size) { case 4: *val = get_unaligned_be32(adis->rx); @@ -222,7 +222,7 @@ int __adis_read_reg(struct adis *adis, unsigned int reg, return ret; } -EXPORT_SYMBOL_GPL(__adis_read_reg); +EXPORT_SYMBOL_NS_GPL(__adis_read_reg, IIO_ADISLIB); /** * __adis_update_bits_base() - ADIS Update bits function - Unlocked version * @adis: The adis device @@ -247,17 +247,17 @@ int __adis_update_bits_base(struct adis *adis, unsigned int reg, const u32 mask, return __adis_write_reg(adis, reg, __val, size); } -EXPORT_SYMBOL_GPL(__adis_update_bits_base); +EXPORT_SYMBOL_NS_GPL(__adis_update_bits_base, IIO_ADISLIB); #ifdef CONFIG_DEBUG_FS -int adis_debugfs_reg_access(struct iio_dev *indio_dev, - unsigned int reg, unsigned int writeval, unsigned int *readval) +int adis_debugfs_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) { struct adis *adis = iio_device_get_drvdata(indio_dev); if (readval) { - uint16_t val16; + u16 val16; int ret; ret = adis_read_reg_16(adis, reg, &val16); @@ -265,36 +265,41 @@ int adis_debugfs_reg_access(struct iio_dev *indio_dev, *readval = val16; return ret; - } else { - return adis_write_reg_16(adis, reg, writeval); } + + return adis_write_reg_16(adis, reg, writeval); } -EXPORT_SYMBOL(adis_debugfs_reg_access); +EXPORT_SYMBOL_NS(adis_debugfs_reg_access, IIO_ADISLIB); #endif /** - * adis_enable_irq() - Enable or disable data ready IRQ + * __adis_enable_irq() - Enable or disable data ready IRQ (unlocked) * @adis: The adis device * @enable: Whether to enable the IRQ * * Returns 0 on success, negative error code otherwise */ -int adis_enable_irq(struct adis *adis, bool enable) +int __adis_enable_irq(struct adis *adis, bool enable) { - int ret = 0; - uint16_t msc; + int ret; + u16 msc; - mutex_lock(&adis->state_lock); + if (adis->data->enable_irq) + return adis->data->enable_irq(adis, enable); - if (adis->data->enable_irq) { - ret = adis->data->enable_irq(adis, enable); - goto out_unlock; + if (adis->data->unmasked_drdy) { + if (enable) + enable_irq(adis->spi->irq); + else + disable_irq(adis->spi->irq); + + return 0; } ret = __adis_read_reg_16(adis, adis->data->msc_ctrl_reg, &msc); if (ret) - goto out_unlock; + return ret; msc |= ADIS_MSC_CTRL_DATA_RDY_POL_HIGH; msc &= ~ADIS_MSC_CTRL_DATA_RDY_DIO2; @@ -303,13 +308,9 @@ int adis_enable_irq(struct adis *adis, bool enable) else msc &= ~ADIS_MSC_CTRL_DATA_RDY_EN; - ret = __adis_write_reg_16(adis, adis->data->msc_ctrl_reg, msc); - -out_unlock: - mutex_unlock(&adis->state_lock); - return ret; + return __adis_write_reg_16(adis, adis->data->msc_ctrl_reg, msc); } -EXPORT_SYMBOL(adis_enable_irq); +EXPORT_SYMBOL_NS(__adis_enable_irq, IIO_ADISLIB); /** * __adis_check_status() - Check the device for error conditions (unlocked) @@ -319,7 +320,7 @@ EXPORT_SYMBOL(adis_enable_irq); */ int __adis_check_status(struct adis *adis) { - uint16_t status; + u16 status; int ret; int i; @@ -341,7 +342,7 @@ int __adis_check_status(struct adis *adis) return -EIO; } -EXPORT_SYMBOL_GPL(__adis_check_status); +EXPORT_SYMBOL_NS_GPL(__adis_check_status, IIO_ADISLIB); /** * __adis_reset() - Reset the device (unlocked version) @@ -355,7 +356,7 @@ int __adis_reset(struct adis *adis) const struct adis_timeout *timeouts = adis->data->timeouts; ret = __adis_write_reg_8(adis, adis->data->glob_cmd_reg, - ADIS_GLOB_CMD_SW_RESET); + ADIS_GLOB_CMD_SW_RESET); if (ret) { dev_err(&adis->spi->dev, "Failed to reset device: %d\n", ret); return ret; @@ -365,7 +366,7 @@ int __adis_reset(struct adis *adis) return 0; } -EXPORT_SYMBOL_GPL(__adis_reset); +EXPORT_SYMBOL_NS_GPL(__adis_reset, IIO_ADIS_LIB); static int adis_self_test(struct adis *adis) { @@ -411,7 +412,7 @@ int __adis_initial_startup(struct adis *adis) { const struct adis_timeout *timeouts = adis->data->timeouts; struct gpio_desc *gpio; - uint16_t prod_id; + u16 prod_id; int ret; /* check if the device has rst pin low */ @@ -420,7 +421,7 @@ int __adis_initial_startup(struct adis *adis) return PTR_ERR(gpio); if (gpio) { - msleep(10); + usleep_range(10, 12); /* bring device out of reset */ gpiod_set_value_cansleep(gpio, 0); msleep(timeouts->reset_ms); @@ -434,7 +435,13 @@ int __adis_initial_startup(struct adis *adis) if (ret) return ret; - adis_enable_irq(adis, false); + /* + * don't bother calling this if we can't unmask the IRQ as in this case + * the IRQ is most likely not yet requested and we will request it + * with 'IRQF_NO_AUTOEN' anyways. + */ + if (!adis->data->unmasked_drdy) + __adis_enable_irq(adis, false); if (!adis->data->prod_id_reg) return 0; @@ -450,7 +457,7 @@ int __adis_initial_startup(struct adis *adis) return 0; } -EXPORT_SYMBOL_GPL(__adis_initial_startup); +EXPORT_SYMBOL_NS_GPL(__adis_initial_startup, IIO_ADISLIB); /** * adis_single_conversion() - Performs a single sample conversion @@ -468,7 +475,8 @@ EXPORT_SYMBOL_GPL(__adis_initial_startup); * a error bit in the channels raw value set error_mask to 0. */ int adis_single_conversion(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, unsigned int error_mask, int *val) + const struct iio_chan_spec *chan, + unsigned int error_mask, int *val) { struct adis *adis = iio_device_get_drvdata(indio_dev); unsigned int uval; @@ -477,7 +485,7 @@ int adis_single_conversion(struct iio_dev *indio_dev, mutex_lock(&adis->state_lock); ret = __adis_read_reg(adis, chan->address, &uval, - chan->scan_type.storagebits / 8); + chan->scan_type.storagebits / 8); if (ret) goto err_unlock; @@ -497,7 +505,7 @@ int adis_single_conversion(struct iio_dev *indio_dev, mutex_unlock(&adis->state_lock); return ret; } -EXPORT_SYMBOL_GPL(adis_single_conversion); +EXPORT_SYMBOL_NS_GPL(adis_single_conversion, IIO_ADISLIB); /** * adis_init() - Initialize adis device structure @@ -512,7 +520,7 @@ EXPORT_SYMBOL_GPL(adis_single_conversion); * called. */ int adis_init(struct adis *adis, struct iio_dev *indio_dev, - struct spi_device *spi, const struct adis_data *data) + struct spi_device *spi, const struct adis_data *data) { if (!data || !data->timeouts) { dev_err(&spi->dev, "No config data or timeouts not defined!\n"); @@ -534,7 +542,7 @@ int adis_init(struct adis *adis, struct iio_dev *indio_dev, return 0; } -EXPORT_SYMBOL_GPL(adis_init); +EXPORT_SYMBOL_NS_GPL(adis_init, IIO_ADISLIB); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Lars-Peter Clausen "); diff --git a/drivers/iio/imu/adis16400.c b/drivers/iio/imu/adis16400.c index 4aff16466da02..c5255116954a7 100644 --- a/drivers/iio/imu/adis16400.c +++ b/drivers/iio/imu/adis16400.c @@ -1252,3 +1252,4 @@ module_spi_driver(adis16400_driver); MODULE_AUTHOR("Manuel Stahl "); MODULE_DESCRIPTION("Analog Devices ADIS16400/5 IMU SPI driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ADISLIB); diff --git a/drivers/iio/imu/adis16460.c b/drivers/iio/imu/adis16460.c index 74a161e397332..a28143a19d3a4 100644 --- a/drivers/iio/imu/adis16460.c +++ b/drivers/iio/imu/adis16460.c @@ -403,12 +403,12 @@ static int adis16460_probe(struct spi_device *spi) if (ret) return ret; + /* We cannot mask the interrupt, so ensure it isn't auto enabled */ + st->adis.irq_flag |= IRQF_NO_AUTOEN; ret = devm_adis_setup_buffer_and_trigger(&st->adis, indio_dev, NULL); if (ret) return ret; - adis16460_enable_irq(&st->adis, 0); - ret = __adis_initial_startup(&st->adis); if (ret) return ret; @@ -447,3 +447,4 @@ module_spi_driver(adis16460_driver); MODULE_AUTHOR("Dragos Bogdan "); MODULE_DESCRIPTION("Analog Devices ADIS16460 IMU driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_ADISLIB); diff --git a/drivers/iio/imu/adis16475.c b/drivers/iio/imu/adis16475.c index 3c4e4deb87608..aed1cf3bfa13c 100644 --- a/drivers/iio/imu/adis16475.c +++ b/drivers/iio/imu/adis16475.c @@ -1196,6 +1196,9 @@ static int adis16475_config_irq_pin(struct adis16475 *st) return -EINVAL; } + /* We cannot mask the interrupt so ensure it's not enabled at request */ + st->adis.irq_flag |= IRQF_NO_AUTOEN; + val = ADIS16475_MSG_CTRL_DR_POL(polarity); ret = __adis_update_bits(&st->adis, ADIS16475_REG_MSG_CTRL, ADIS16475_MSG_CTRL_DR_POL_MASK, val); @@ -1300,8 +1303,6 @@ static int adis16475_probe(struct spi_device *spi) if (ret) return ret; - adis16475_enable_irq(&st->adis, false); - ret = devm_iio_device_register(&spi->dev, indio_dev); if (ret) return ret; @@ -1323,3 +1324,4 @@ module_spi_driver(adis16475_driver); MODULE_AUTHOR("Nuno Sa "); MODULE_DESCRIPTION("Analog Devices ADIS16475 IMU driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_ADISLIB); diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c index dfe86c5893254..c6a3d9a04fce3 100644 --- a/drivers/iio/imu/adis16480.c +++ b/drivers/iio/imu/adis16480.c @@ -1340,3 +1340,4 @@ module_spi_driver(adis16480_driver); MODULE_AUTHOR("Lars-Peter Clausen "); MODULE_DESCRIPTION("Analog Devices ADIS16480 IMU driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_ADISLIB); diff --git a/drivers/iio/imu/adis_buffer.c b/drivers/iio/imu/adis_buffer.c index 175af154e4437..7cc1145910f68 100644 --- a/drivers/iio/imu/adis_buffer.c +++ b/drivers/iio/imu/adis_buffer.c @@ -20,7 +20,7 @@ #include static int adis_update_scan_mode_burst(struct iio_dev *indio_dev, - const unsigned long *scan_mask) + const unsigned long *scan_mask) { struct adis *adis = iio_device_get_drvdata(indio_dev); unsigned int burst_length, burst_max_length; @@ -63,7 +63,7 @@ static int adis_update_scan_mode_burst(struct iio_dev *indio_dev, } int adis_update_scan_mode(struct iio_dev *indio_dev, - const unsigned long *scan_mask) + const unsigned long *scan_mask) { struct adis *adis = iio_device_get_drvdata(indio_dev); const struct iio_chan_spec *chan; @@ -120,7 +120,7 @@ int adis_update_scan_mode(struct iio_dev *indio_dev, return 0; } -EXPORT_SYMBOL_GPL(adis_update_scan_mode); +EXPORT_SYMBOL_NS_GPL(adis_update_scan_mode, IIO_ADISLIB); static irqreturn_t adis_trigger_handler(int irq, void *p) { @@ -149,7 +149,7 @@ static irqreturn_t adis_trigger_handler(int irq, void *p) } iio_push_to_buffers_with_timestamp(indio_dev, adis->buffer, - pf->timestamp); + pf->timestamp); iio_trigger_notify_done(indio_dev->trig); @@ -202,5 +202,5 @@ devm_adis_setup_buffer_and_trigger(struct adis *adis, struct iio_dev *indio_dev, return devm_add_action_or_reset(&adis->spi->dev, adis_buffer_cleanup, adis); } -EXPORT_SYMBOL_GPL(devm_adis_setup_buffer_and_trigger); +EXPORT_SYMBOL_NS_GPL(devm_adis_setup_buffer_and_trigger, IIO_ADISLIB); diff --git a/drivers/iio/imu/adis_trigger.c b/drivers/iio/imu/adis_trigger.c index 64e0ba51cb18e..80adfa58e50cb 100644 --- a/drivers/iio/imu/adis_trigger.c +++ b/drivers/iio/imu/adis_trigger.c @@ -15,8 +15,7 @@ #include #include -static int adis_data_rdy_trigger_set_state(struct iio_trigger *trig, - bool state) +static int adis_data_rdy_trigger_set_state(struct iio_trigger *trig, bool state) { struct adis *adis = iio_trigger_get_drvdata(trig); @@ -36,18 +35,23 @@ static void adis_trigger_setup(struct adis *adis) static int adis_validate_irq_flag(struct adis *adis) { + unsigned long direction = adis->irq_flag & IRQF_TRIGGER_MASK; + + /* We cannot mask the interrupt so ensure it's not enabled at request */ + if (adis->data->unmasked_drdy) + adis->irq_flag |= IRQF_NO_AUTOEN; /* * Typically this devices have data ready either on the rising edge or * on the falling edge of the data ready pin. This checks enforces that * one of those is set in the drivers... It defaults to - * IRQF_TRIGGER_RISING for backward compatibility wiht devices that + * IRQF_TRIGGER_RISING for backward compatibility with devices that * don't support changing the pin polarity. */ - if (!adis->irq_flag) { - adis->irq_flag = IRQF_TRIGGER_RISING; + if (direction == IRQF_TRIGGER_NONE) { + adis->irq_flag |= IRQF_TRIGGER_RISING; return 0; - } else if (adis->irq_flag != IRQF_TRIGGER_RISING && - adis->irq_flag != IRQF_TRIGGER_FALLING) { + } else if (direction != IRQF_TRIGGER_RISING && + direction != IRQF_TRIGGER_FALLING) { dev_err(&adis->spi->dev, "Invalid IRQ mask: %08lx\n", adis->irq_flag); return -EINVAL; @@ -88,5 +92,5 @@ int devm_adis_probe_trigger(struct adis *adis, struct iio_dev *indio_dev) return devm_iio_trigger_register(&adis->spi->dev, adis->trig); } -EXPORT_SYMBOL_GPL(devm_adis_probe_trigger); +EXPORT_SYMBOL_NS_GPL(devm_adis_probe_trigger, IIO_ADISLIB); diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c index 8306daa779081..b2ae2d2c7eefc 100644 --- a/drivers/iio/temperature/ltc2983.c +++ b/drivers/iio/temperature/ltc2983.c @@ -205,6 +205,7 @@ struct ltc2983_data { * Holds the converted temperature */ __be32 temp ____cacheline_aligned; + __be32 chan_val; }; struct ltc2983_sensor { @@ -309,19 +310,18 @@ static int __ltc2983_fault_handler(const struct ltc2983_data *st, return 0; } -static int __ltc2983_chan_assign_common(const struct ltc2983_data *st, +static int __ltc2983_chan_assign_common(struct ltc2983_data *st, const struct ltc2983_sensor *sensor, u32 chan_val) { u32 reg = LTC2983_CHAN_START_ADDR(sensor->chan); - __be32 __chan_val; chan_val |= LTC2983_CHAN_TYPE(sensor->type); dev_dbg(&st->spi->dev, "Assign reg:0x%04X, val:0x%08X\n", reg, chan_val); - __chan_val = cpu_to_be32(chan_val); - return regmap_bulk_write(st->regmap, reg, &__chan_val, - sizeof(__chan_val)); + st->chan_val = cpu_to_be32(chan_val); + return regmap_bulk_write(st->regmap, reg, &st->chan_val, + sizeof(st->chan_val)); } static int __ltc2983_chan_custom_sensor_assign(struct ltc2983_data *st, diff --git a/drivers/staging/iio/accel/adis16203.c b/drivers/staging/iio/accel/adis16203.c index b68304da288b5..7be44ff2c943a 100644 --- a/drivers/staging/iio/accel/adis16203.c +++ b/drivers/staging/iio/accel/adis16203.c @@ -318,3 +318,4 @@ MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); MODULE_DESCRIPTION("Analog Devices ADIS16203 Programmable 360 Degrees Inclinometer"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("spi:adis16203"); +MODULE_IMPORT_NS(IIO_ADISLIB); diff --git a/drivers/staging/iio/accel/adis16240.c b/drivers/staging/iio/accel/adis16240.c index 5064adce5f583..dbbbf81207f9c 100644 --- a/drivers/staging/iio/accel/adis16240.c +++ b/drivers/staging/iio/accel/adis16240.c @@ -445,3 +445,4 @@ MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); MODULE_DESCRIPTION("Analog Devices Programmable Impact Sensor and Recorder"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("spi:adis16240"); +MODULE_IMPORT_NS(IIO_ADISLIB); diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h index 04e96d688ba9e..5f45b785e794c 100644 --- a/include/linux/iio/imu/adis.h +++ b/include/linux/iio/imu/adis.h @@ -32,6 +32,7 @@ struct adis_timeout { u16 sw_reset_ms; u16 self_test_ms; }; + /** * struct adis_data - ADIS chip variant specific data * @read_delay: SPI delay for read operations in us @@ -45,10 +46,11 @@ struct adis_timeout { * @self_test_mask: Bitmask of supported self-test operations * @self_test_reg: Register address to request self test command * @self_test_no_autoclear: True if device's self-test needs clear of ctrl reg - * @status_error_msgs: Array of error messgaes + * @status_error_msgs: Array of error messages * @status_error_mask: Bitmask of errors supported by the device * @timeouts: Chip specific delays * @enable_irq: Hook for ADIS devices that have a special IRQ enable/disable + * @unmasked_drdy: True for devices that cannot mask/unmask the data ready pin * @has_paging: True if ADIS device has paged registers * @burst_reg_cmd: Register command that triggers burst * @burst_len: Burst size in the SPI RX buffer. If @burst_max_len is defined, @@ -77,6 +79,7 @@ struct adis_data { unsigned int status_error_mask; int (*enable_irq)(struct adis *adis, bool enable); + bool unmasked_drdy; bool has_paging; @@ -126,12 +129,12 @@ struct adis { unsigned long irq_flag; void *buffer; - uint8_t tx[10] ____cacheline_aligned; - uint8_t rx[4]; + u8 tx[10] ____cacheline_aligned; + u8 rx[4]; }; int adis_init(struct adis *adis, struct iio_dev *indio_dev, - struct spi_device *spi, const struct adis_data *data); + struct spi_device *spi, const struct adis_data *data); int __adis_reset(struct adis *adis); /** @@ -152,9 +155,9 @@ static inline int adis_reset(struct adis *adis) } int __adis_write_reg(struct adis *adis, unsigned int reg, - unsigned int val, unsigned int size); + unsigned int val, unsigned int size); int __adis_read_reg(struct adis *adis, unsigned int reg, - unsigned int *val, unsigned int size); + unsigned int *val, unsigned int size); /** * __adis_write_reg_8() - Write single byte to a register (unlocked) @@ -163,7 +166,7 @@ int __adis_read_reg(struct adis *adis, unsigned int reg, * @value: The value to write */ static inline int __adis_write_reg_8(struct adis *adis, unsigned int reg, - uint8_t val) + u8 val) { return __adis_write_reg(adis, reg, val, 1); } @@ -175,7 +178,7 @@ static inline int __adis_write_reg_8(struct adis *adis, unsigned int reg, * @value: Value to be written */ static inline int __adis_write_reg_16(struct adis *adis, unsigned int reg, - uint16_t val) + u16 val) { return __adis_write_reg(adis, reg, val, 2); } @@ -187,7 +190,7 @@ static inline int __adis_write_reg_16(struct adis *adis, unsigned int reg, * @value: Value to be written */ static inline int __adis_write_reg_32(struct adis *adis, unsigned int reg, - uint32_t val) + u32 val) { return __adis_write_reg(adis, reg, val, 4); } @@ -199,7 +202,7 @@ static inline int __adis_write_reg_32(struct adis *adis, unsigned int reg, * @val: The value read back from the device */ static inline int __adis_read_reg_16(struct adis *adis, unsigned int reg, - uint16_t *val) + u16 *val) { unsigned int tmp; int ret; @@ -218,7 +221,7 @@ static inline int __adis_read_reg_16(struct adis *adis, unsigned int reg, * @val: The value read back from the device */ static inline int __adis_read_reg_32(struct adis *adis, unsigned int reg, - uint32_t *val) + u32 *val) { unsigned int tmp; int ret; @@ -238,7 +241,7 @@ static inline int __adis_read_reg_32(struct adis *adis, unsigned int reg, * @size: The size of the @value (in bytes) */ static inline int adis_write_reg(struct adis *adis, unsigned int reg, - unsigned int val, unsigned int size) + unsigned int val, unsigned int size) { int ret; @@ -257,7 +260,7 @@ static inline int adis_write_reg(struct adis *adis, unsigned int reg, * @size: The size of the @val buffer */ static int adis_read_reg(struct adis *adis, unsigned int reg, - unsigned int *val, unsigned int size) + unsigned int *val, unsigned int size) { int ret; @@ -275,7 +278,7 @@ static int adis_read_reg(struct adis *adis, unsigned int reg, * @value: The value to write */ static inline int adis_write_reg_8(struct adis *adis, unsigned int reg, - uint8_t val) + u8 val) { return adis_write_reg(adis, reg, val, 1); } @@ -287,7 +290,7 @@ static inline int adis_write_reg_8(struct adis *adis, unsigned int reg, * @value: Value to be written */ static inline int adis_write_reg_16(struct adis *adis, unsigned int reg, - uint16_t val) + u16 val) { return adis_write_reg(adis, reg, val, 2); } @@ -299,7 +302,7 @@ static inline int adis_write_reg_16(struct adis *adis, unsigned int reg, * @value: Value to be written */ static inline int adis_write_reg_32(struct adis *adis, unsigned int reg, - uint32_t val) + u32 val) { return adis_write_reg(adis, reg, val, 4); } @@ -311,7 +314,7 @@ static inline int adis_write_reg_32(struct adis *adis, unsigned int reg, * @val: The value read back from the device */ static inline int adis_read_reg_16(struct adis *adis, unsigned int reg, - uint16_t *val) + u16 *val) { unsigned int tmp; int ret; @@ -330,7 +333,7 @@ static inline int adis_read_reg_16(struct adis *adis, unsigned int reg, * @val: The value read back from the device */ static inline int adis_read_reg_32(struct adis *adis, unsigned int reg, - uint32_t *val) + u32 *val) { unsigned int tmp; int ret; @@ -401,9 +404,20 @@ static inline int adis_update_bits_base(struct adis *adis, unsigned int reg, __adis_update_bits_base(adis, reg, mask, val, 2)); \ }) -int adis_enable_irq(struct adis *adis, bool enable); int __adis_check_status(struct adis *adis); int __adis_initial_startup(struct adis *adis); +int __adis_enable_irq(struct adis *adis, bool enable); + +static inline int adis_enable_irq(struct adis *adis, bool enable) +{ + int ret; + + mutex_lock(&adis->state_lock); + ret = __adis_enable_irq(adis, enable); + mutex_unlock(&adis->state_lock); + + return ret; +} static inline int adis_check_status(struct adis *adis) { @@ -429,8 +443,8 @@ static inline int adis_initial_startup(struct adis *adis) } int adis_single_conversion(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, unsigned int error_mask, - int *val); + const struct iio_chan_spec *chan, + unsigned int error_mask, int *val); #define ADIS_VOLTAGE_CHAN(addr, si, chan, name, info_all, bits) { \ .type = IIO_VOLTAGE, \ @@ -479,7 +493,7 @@ int adis_single_conversion(struct iio_dev *indio_dev, .modified = 1, \ .channel2 = IIO_MOD_ ## mod, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ - info_sep, \ + (info_sep), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ .info_mask_shared_by_all = info_all, \ .address = (addr), \ @@ -513,7 +527,7 @@ devm_adis_setup_buffer_and_trigger(struct adis *adis, struct iio_dev *indio_dev, int devm_adis_probe_trigger(struct adis *adis, struct iio_dev *indio_dev); int adis_update_scan_mode(struct iio_dev *indio_dev, - const unsigned long *scan_mask); + const unsigned long *scan_mask); #else /* CONFIG_IIO_BUFFER */ @@ -537,7 +551,8 @@ static inline int devm_adis_probe_trigger(struct adis *adis, #ifdef CONFIG_DEBUG_FS int adis_debugfs_reg_access(struct iio_dev *indio_dev, - unsigned int reg, unsigned int writeval, unsigned int *readval); + unsigned int reg, unsigned int writeval, + unsigned int *readval); #else diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index ee8299eb1f524..0652b4858ba62 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -61,6 +61,9 @@ * interrupt handler after suspending interrupts. For system * wakeup devices users need to implement wakeup detection in * their interrupt handlers. + * IRQF_NO_AUTOEN - Don't enable IRQ or NMI automatically when users request it. + * Users will enable it explicitly by enable_irq() or enable_nmi() + * later. */ #define IRQF_SHARED 0x00000080 #define IRQF_PROBE_SHARED 0x00000100 @@ -74,6 +77,7 @@ #define IRQF_NO_THREAD 0x00010000 #define IRQF_EARLY_RESUME 0x00020000 #define IRQF_COND_SUSPEND 0x00040000 +#define IRQF_NO_AUTOEN 0x00080000 #define IRQF_TIMER (__IRQF_TIMER | IRQF_NO_SUSPEND | IRQF_NO_THREAD) From e27451e7d5e1d2642b495fa641e506f4b9d49372 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Fri, 3 Feb 2023 19:27:13 -0600 Subject: [PATCH 20/65] backports: it66121: from: linux.git Reference: v5.19.17 Signed-off-by: Robert Nelson --- drivers/gpu/drm/bridge/ite-it66121.c | 1662 ++++++++++++++++++++++++++ 1 file changed, 1662 insertions(+) create mode 100644 drivers/gpu/drm/bridge/ite-it66121.c diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c new file mode 100644 index 0000000000000..448c58e60c112 --- /dev/null +++ b/drivers/gpu/drm/bridge/ite-it66121.c @@ -0,0 +1,1662 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020 BayLibre, SAS + * Author: Phong LE + * Copyright (C) 2018-2019, Artem Mygaiev + * Copyright (C) 2017, Fresco Logic, Incorporated. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define IT66121_VENDOR_ID0_REG 0x00 +#define IT66121_VENDOR_ID1_REG 0x01 +#define IT66121_DEVICE_ID0_REG 0x02 +#define IT66121_DEVICE_ID1_REG 0x03 + +#define IT66121_VENDOR_ID0 0x54 +#define IT66121_VENDOR_ID1 0x49 +#define IT66121_DEVICE_ID0 0x12 +#define IT66121_DEVICE_ID1 0x06 +#define IT66121_REVISION_MASK GENMASK(7, 4) +#define IT66121_DEVICE_ID1_MASK GENMASK(3, 0) + +#define IT66121_MASTER_SEL_REG 0x10 +#define IT66121_MASTER_SEL_HOST BIT(0) + +#define IT66121_AFE_DRV_REG 0x61 +#define IT66121_AFE_DRV_RST BIT(4) +#define IT66121_AFE_DRV_PWD BIT(5) + +#define IT66121_INPUT_MODE_REG 0x70 +#define IT66121_INPUT_MODE_RGB (0 << 6) +#define IT66121_INPUT_MODE_YUV422 BIT(6) +#define IT66121_INPUT_MODE_YUV444 (2 << 6) +#define IT66121_INPUT_MODE_CCIR656 BIT(4) +#define IT66121_INPUT_MODE_SYNCEMB BIT(3) +#define IT66121_INPUT_MODE_DDR BIT(2) + +#define IT66121_INPUT_CSC_REG 0x72 +#define IT66121_INPUT_CSC_ENDITHER BIT(7) +#define IT66121_INPUT_CSC_ENUDFILTER BIT(6) +#define IT66121_INPUT_CSC_DNFREE_GO BIT(5) +#define IT66121_INPUT_CSC_RGB_TO_YUV 0x02 +#define IT66121_INPUT_CSC_YUV_TO_RGB 0x03 +#define IT66121_INPUT_CSC_NO_CONV 0x00 + +#define IT66121_AFE_XP_REG 0x62 +#define IT66121_AFE_XP_GAINBIT BIT(7) +#define IT66121_AFE_XP_PWDPLL BIT(6) +#define IT66121_AFE_XP_ENI BIT(5) +#define IT66121_AFE_XP_ENO BIT(4) +#define IT66121_AFE_XP_RESETB BIT(3) +#define IT66121_AFE_XP_PWDI BIT(2) + +#define IT66121_AFE_IP_REG 0x64 +#define IT66121_AFE_IP_GAINBIT BIT(7) +#define IT66121_AFE_IP_PWDPLL BIT(6) +#define IT66121_AFE_IP_CKSEL_05 (0 << 4) +#define IT66121_AFE_IP_CKSEL_1 BIT(4) +#define IT66121_AFE_IP_CKSEL_2 (2 << 4) +#define IT66121_AFE_IP_CKSEL_2OR4 (3 << 4) +#define IT66121_AFE_IP_ER0 BIT(3) +#define IT66121_AFE_IP_RESETB BIT(2) +#define IT66121_AFE_IP_ENC BIT(1) +#define IT66121_AFE_IP_EC1 BIT(0) + +#define IT66121_AFE_XP_EC1_REG 0x68 +#define IT66121_AFE_XP_EC1_LOWCLK BIT(4) + +#define IT66121_SW_RST_REG 0x04 +#define IT66121_SW_RST_REF BIT(5) +#define IT66121_SW_RST_AREF BIT(4) +#define IT66121_SW_RST_VID BIT(3) +#define IT66121_SW_RST_AUD BIT(2) +#define IT66121_SW_RST_HDCP BIT(0) + +#define IT66121_DDC_COMMAND_REG 0x15 +#define IT66121_DDC_COMMAND_BURST_READ 0x0 +#define IT66121_DDC_COMMAND_EDID_READ 0x3 +#define IT66121_DDC_COMMAND_FIFO_CLR 0x9 +#define IT66121_DDC_COMMAND_SCL_PULSE 0xA +#define IT66121_DDC_COMMAND_ABORT 0xF + +#define IT66121_HDCP_REG 0x20 +#define IT66121_HDCP_CPDESIRED BIT(0) +#define IT66121_HDCP_EN1P1FEAT BIT(1) + +#define IT66121_INT_STATUS1_REG 0x06 +#define IT66121_INT_STATUS1_AUD_OVF BIT(7) +#define IT66121_INT_STATUS1_DDC_NOACK BIT(5) +#define IT66121_INT_STATUS1_DDC_FIFOERR BIT(4) +#define IT66121_INT_STATUS1_DDC_BUSHANG BIT(2) +#define IT66121_INT_STATUS1_RX_SENS_STATUS BIT(1) +#define IT66121_INT_STATUS1_HPD_STATUS BIT(0) + +#define IT66121_DDC_HEADER_REG 0x11 +#define IT66121_DDC_HEADER_HDCP 0x74 +#define IT66121_DDC_HEADER_EDID 0xA0 + +#define IT66121_DDC_OFFSET_REG 0x12 +#define IT66121_DDC_BYTE_REG 0x13 +#define IT66121_DDC_SEGMENT_REG 0x14 +#define IT66121_DDC_RD_FIFO_REG 0x17 + +#define IT66121_CLK_BANK_REG 0x0F +#define IT66121_CLK_BANK_PWROFF_RCLK BIT(6) +#define IT66121_CLK_BANK_PWROFF_ACLK BIT(5) +#define IT66121_CLK_BANK_PWROFF_TXCLK BIT(4) +#define IT66121_CLK_BANK_PWROFF_CRCLK BIT(3) +#define IT66121_CLK_BANK_0 0 +#define IT66121_CLK_BANK_1 1 + +#define IT66121_INT_REG 0x05 +#define IT66121_INT_ACTIVE_HIGH BIT(7) +#define IT66121_INT_OPEN_DRAIN BIT(6) +#define IT66121_INT_TX_CLK_OFF BIT(0) + +#define IT66121_INT_MASK1_REG 0x09 +#define IT66121_INT_MASK1_AUD_OVF BIT(7) +#define IT66121_INT_MASK1_DDC_NOACK BIT(5) +#define IT66121_INT_MASK1_DDC_FIFOERR BIT(4) +#define IT66121_INT_MASK1_DDC_BUSHANG BIT(2) +#define IT66121_INT_MASK1_RX_SENS BIT(1) +#define IT66121_INT_MASK1_HPD BIT(0) + +#define IT66121_INT_CLR1_REG 0x0C +#define IT66121_INT_CLR1_PKTACP BIT(7) +#define IT66121_INT_CLR1_PKTNULL BIT(6) +#define IT66121_INT_CLR1_PKTGEN BIT(5) +#define IT66121_INT_CLR1_KSVLISTCHK BIT(4) +#define IT66121_INT_CLR1_AUTHDONE BIT(3) +#define IT66121_INT_CLR1_AUTHFAIL BIT(2) +#define IT66121_INT_CLR1_RX_SENS BIT(1) +#define IT66121_INT_CLR1_HPD BIT(0) + +#define IT66121_AV_MUTE_REG 0xC1 +#define IT66121_AV_MUTE_ON BIT(0) +#define IT66121_AV_MUTE_BLUESCR BIT(1) + +#define IT66121_PKT_CTS_CTRL_REG 0xC5 +#define IT66121_PKT_CTS_CTRL_SEL BIT(1) + +#define IT66121_PKT_GEN_CTRL_REG 0xC6 +#define IT66121_PKT_GEN_CTRL_ON BIT(0) +#define IT66121_PKT_GEN_CTRL_RPT BIT(1) + +#define IT66121_AVIINFO_DB1_REG 0x158 +#define IT66121_AVIINFO_DB2_REG 0x159 +#define IT66121_AVIINFO_DB3_REG 0x15A +#define IT66121_AVIINFO_DB4_REG 0x15B +#define IT66121_AVIINFO_DB5_REG 0x15C +#define IT66121_AVIINFO_CSUM_REG 0x15D +#define IT66121_AVIINFO_DB6_REG 0x15E +#define IT66121_AVIINFO_DB7_REG 0x15F +#define IT66121_AVIINFO_DB8_REG 0x160 +#define IT66121_AVIINFO_DB9_REG 0x161 +#define IT66121_AVIINFO_DB10_REG 0x162 +#define IT66121_AVIINFO_DB11_REG 0x163 +#define IT66121_AVIINFO_DB12_REG 0x164 +#define IT66121_AVIINFO_DB13_REG 0x165 + +#define IT66121_AVI_INFO_PKT_REG 0xCD +#define IT66121_AVI_INFO_PKT_ON BIT(0) +#define IT66121_AVI_INFO_PKT_RPT BIT(1) + +#define IT66121_HDMI_MODE_REG 0xC0 +#define IT66121_HDMI_MODE_HDMI BIT(0) + +#define IT66121_SYS_STATUS_REG 0x0E +#define IT66121_SYS_STATUS_ACTIVE_IRQ BIT(7) +#define IT66121_SYS_STATUS_HPDETECT BIT(6) +#define IT66121_SYS_STATUS_SENDECTECT BIT(5) +#define IT66121_SYS_STATUS_VID_STABLE BIT(4) +#define IT66121_SYS_STATUS_AUD_CTS_CLR BIT(1) +#define IT66121_SYS_STATUS_CLEAR_IRQ BIT(0) + +#define IT66121_DDC_STATUS_REG 0x16 +#define IT66121_DDC_STATUS_TX_DONE BIT(7) +#define IT66121_DDC_STATUS_ACTIVE BIT(6) +#define IT66121_DDC_STATUS_NOACK BIT(5) +#define IT66121_DDC_STATUS_WAIT_BUS BIT(4) +#define IT66121_DDC_STATUS_ARBI_LOSE BIT(3) +#define IT66121_DDC_STATUS_FIFO_FULL BIT(2) +#define IT66121_DDC_STATUS_FIFO_EMPTY BIT(1) +#define IT66121_DDC_STATUS_FIFO_VALID BIT(0) + +#define IT66121_EDID_SLEEP_US 20000 +#define IT66121_EDID_TIMEOUT_US 200000 +#define IT66121_EDID_FIFO_SIZE 32 + +#define IT66121_CLK_CTRL0_REG 0x58 +#define IT66121_CLK_CTRL0_AUTO_OVER_SAMPLING BIT(4) +#define IT66121_CLK_CTRL0_EXT_MCLK_MASK GENMASK(3, 2) +#define IT66121_CLK_CTRL0_EXT_MCLK_128FS (0 << 2) +#define IT66121_CLK_CTRL0_EXT_MCLK_256FS BIT(2) +#define IT66121_CLK_CTRL0_EXT_MCLK_512FS (2 << 2) +#define IT66121_CLK_CTRL0_EXT_MCLK_1024FS (3 << 2) +#define IT66121_CLK_CTRL0_AUTO_IPCLK BIT(0) +#define IT66121_CLK_STATUS1_REG 0x5E +#define IT66121_CLK_STATUS2_REG 0x5F + +#define IT66121_AUD_CTRL0_REG 0xE0 +#define IT66121_AUD_SWL (3 << 6) +#define IT66121_AUD_16BIT (0 << 6) +#define IT66121_AUD_18BIT BIT(6) +#define IT66121_AUD_20BIT (2 << 6) +#define IT66121_AUD_24BIT (3 << 6) +#define IT66121_AUD_SPDIFTC BIT(5) +#define IT66121_AUD_SPDIF BIT(4) +#define IT66121_AUD_I2S (0 << 4) +#define IT66121_AUD_EN_I2S3 BIT(3) +#define IT66121_AUD_EN_I2S2 BIT(2) +#define IT66121_AUD_EN_I2S1 BIT(1) +#define IT66121_AUD_EN_I2S0 BIT(0) +#define IT66121_AUD_CTRL0_AUD_SEL BIT(4) + +#define IT66121_AUD_CTRL1_REG 0xE1 +#define IT66121_AUD_FIFOMAP_REG 0xE2 +#define IT66121_AUD_CTRL3_REG 0xE3 +#define IT66121_AUD_SRCVALID_FLAT_REG 0xE4 +#define IT66121_AUD_FLAT_SRC0 BIT(4) +#define IT66121_AUD_FLAT_SRC1 BIT(5) +#define IT66121_AUD_FLAT_SRC2 BIT(6) +#define IT66121_AUD_FLAT_SRC3 BIT(7) +#define IT66121_AUD_HDAUDIO_REG 0xE5 + +#define IT66121_AUD_PKT_CTS0_REG 0x130 +#define IT66121_AUD_PKT_CTS1_REG 0x131 +#define IT66121_AUD_PKT_CTS2_REG 0x132 +#define IT66121_AUD_PKT_N0_REG 0x133 +#define IT66121_AUD_PKT_N1_REG 0x134 +#define IT66121_AUD_PKT_N2_REG 0x135 + +#define IT66121_AUD_CHST_MODE_REG 0x191 +#define IT66121_AUD_CHST_CAT_REG 0x192 +#define IT66121_AUD_CHST_SRCNUM_REG 0x193 +#define IT66121_AUD_CHST_CHTNUM_REG 0x194 +#define IT66121_AUD_CHST_CA_FS_REG 0x198 +#define IT66121_AUD_CHST_OFS_WL_REG 0x199 + +#define IT66121_AUD_PKT_CTS_CNT0_REG 0x1A0 +#define IT66121_AUD_PKT_CTS_CNT1_REG 0x1A1 +#define IT66121_AUD_PKT_CTS_CNT2_REG 0x1A2 + +#define IT66121_AUD_FS_22P05K 0x4 +#define IT66121_AUD_FS_44P1K 0x0 +#define IT66121_AUD_FS_88P2K 0x8 +#define IT66121_AUD_FS_176P4K 0xC +#define IT66121_AUD_FS_24K 0x6 +#define IT66121_AUD_FS_48K 0x2 +#define IT66121_AUD_FS_96K 0xA +#define IT66121_AUD_FS_192K 0xE +#define IT66121_AUD_FS_768K 0x9 +#define IT66121_AUD_FS_32K 0x3 +#define IT66121_AUD_FS_OTHER 0x1 + +#define IT66121_AUD_SWL_21BIT 0xD +#define IT66121_AUD_SWL_24BIT 0xB +#define IT66121_AUD_SWL_23BIT 0x9 +#define IT66121_AUD_SWL_22BIT 0x5 +#define IT66121_AUD_SWL_20BIT 0x3 +#define IT66121_AUD_SWL_17BIT 0xC +#define IT66121_AUD_SWL_19BIT 0x8 +#define IT66121_AUD_SWL_18BIT 0x4 +#define IT66121_AUD_SWL_16BIT 0x2 +#define IT66121_AUD_SWL_NOT_INDICATED 0x0 + +#define IT66121_VENDOR_ID0 0x54 +#define IT66121_VENDOR_ID1 0x49 +#define IT66121_DEVICE_ID0 0x12 +#define IT66121_DEVICE_ID1 0x06 +#define IT66121_DEVICE_MASK 0x0F +#define IT66121_AFE_CLK_HIGH 80000 /* Khz */ + +struct it66121_ctx { + struct regmap *regmap; + struct drm_bridge bridge; + struct drm_bridge *next_bridge; + struct drm_connector *connector; + struct device *dev; + struct gpio_desc *gpio_reset; + struct i2c_client *client; + struct regulator_bulk_data supplies[3]; + u32 bus_width; + struct mutex lock; /* Protects fields below and device registers */ + struct hdmi_avi_infoframe hdmi_avi_infoframe; + struct { + struct platform_device *pdev; + u8 ch_enable; + u8 fs; + u8 swl; + bool auto_cts; + } audio; +}; + +static const struct regmap_range_cfg it66121_regmap_banks[] = { + { + .name = "it66121", + .range_min = 0x00, + .range_max = 0x1FF, + .selector_reg = IT66121_CLK_BANK_REG, + .selector_mask = 0x1, + .selector_shift = 0, + .window_start = 0x00, + .window_len = 0x100, + }, +}; + +static const struct regmap_config it66121_regmap_config = { + .val_bits = 8, + .reg_bits = 8, + .max_register = 0x1FF, + .ranges = it66121_regmap_banks, + .num_ranges = ARRAY_SIZE(it66121_regmap_banks), +}; + +static void it66121_hw_reset(struct it66121_ctx *ctx) +{ + gpiod_set_value(ctx->gpio_reset, 1); + msleep(20); + gpiod_set_value(ctx->gpio_reset, 0); +} + +static inline int ite66121_power_on(struct it66121_ctx *ctx) +{ + return regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); +} + +static inline int ite66121_power_off(struct it66121_ctx *ctx) +{ + return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); +} + +static inline int it66121_preamble_ddc(struct it66121_ctx *ctx) +{ + return regmap_write(ctx->regmap, IT66121_MASTER_SEL_REG, IT66121_MASTER_SEL_HOST); +} + +static inline int it66121_fire_afe(struct it66121_ctx *ctx) +{ + return regmap_write(ctx->regmap, IT66121_AFE_DRV_REG, 0); +} + +/* TOFIX: Handle YCbCr Input & Output */ +static int it66121_configure_input(struct it66121_ctx *ctx) +{ + int ret; + u8 mode = IT66121_INPUT_MODE_RGB; + + if (ctx->bus_width == 12) + mode |= IT66121_INPUT_MODE_DDR; + + ret = regmap_write(ctx->regmap, IT66121_INPUT_MODE_REG, mode); + if (ret) + return ret; + + return regmap_write(ctx->regmap, IT66121_INPUT_CSC_REG, IT66121_INPUT_CSC_NO_CONV); +} + +/** + * it66121_configure_afe() - Configure the analog front end + * @ctx: it66121_ctx object + * @mode: mode to configure + * + * RETURNS: + * zero if success, a negative error code otherwise. + */ +static int it66121_configure_afe(struct it66121_ctx *ctx, + const struct drm_display_mode *mode) +{ + int ret; + + ret = regmap_write(ctx->regmap, IT66121_AFE_DRV_REG, + IT66121_AFE_DRV_RST); + if (ret) + return ret; + + if (mode->clock > IT66121_AFE_CLK_HIGH) { + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_REG, + IT66121_AFE_XP_GAINBIT | + IT66121_AFE_XP_ENO, + IT66121_AFE_XP_GAINBIT); + if (ret) + return ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG, + IT66121_AFE_IP_GAINBIT | + IT66121_AFE_IP_ER0 | + IT66121_AFE_IP_EC1, + IT66121_AFE_IP_GAINBIT); + if (ret) + return ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_EC1_REG, + IT66121_AFE_XP_EC1_LOWCLK, 0x80); + if (ret) + return ret; + } else { + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_REG, + IT66121_AFE_XP_GAINBIT | + IT66121_AFE_XP_ENO, + IT66121_AFE_XP_ENO); + if (ret) + return ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG, + IT66121_AFE_IP_GAINBIT | + IT66121_AFE_IP_ER0 | + IT66121_AFE_IP_EC1, IT66121_AFE_IP_ER0 | + IT66121_AFE_IP_EC1); + if (ret) + return ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_EC1_REG, + IT66121_AFE_XP_EC1_LOWCLK, + IT66121_AFE_XP_EC1_LOWCLK); + if (ret) + return ret; + } + + /* Clear reset flags */ + ret = regmap_write_bits(ctx->regmap, IT66121_SW_RST_REG, + IT66121_SW_RST_REF | IT66121_SW_RST_VID, 0); + if (ret) + return ret; + + return it66121_fire_afe(ctx); +} + +static inline int it66121_wait_ddc_ready(struct it66121_ctx *ctx) +{ + int ret, val; + u32 busy = IT66121_DDC_STATUS_NOACK | IT66121_DDC_STATUS_WAIT_BUS | + IT66121_DDC_STATUS_ARBI_LOSE; + + ret = regmap_read_poll_timeout(ctx->regmap, IT66121_DDC_STATUS_REG, val, true, + IT66121_EDID_SLEEP_US, IT66121_EDID_TIMEOUT_US); + if (ret) + return ret; + + if (val & busy) + return -EAGAIN; + + return 0; +} + +static int it66121_clear_ddc_fifo(struct it66121_ctx *ctx) +{ + int ret; + + ret = it66121_preamble_ddc(ctx); + if (ret) + return ret; + + return regmap_write(ctx->regmap, IT66121_DDC_COMMAND_REG, + IT66121_DDC_COMMAND_FIFO_CLR); +} + +static int it66121_abort_ddc_ops(struct it66121_ctx *ctx) +{ + int ret; + unsigned int swreset, cpdesire; + + ret = regmap_read(ctx->regmap, IT66121_SW_RST_REG, &swreset); + if (ret) + return ret; + + ret = regmap_read(ctx->regmap, IT66121_HDCP_REG, &cpdesire); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_HDCP_REG, + cpdesire & (~IT66121_HDCP_CPDESIRED & 0xFF)); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_SW_RST_REG, + (swreset | IT66121_SW_RST_HDCP)); + if (ret) + return ret; + + ret = it66121_preamble_ddc(ctx); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_DDC_COMMAND_REG, + IT66121_DDC_COMMAND_ABORT); + if (ret) + return ret; + + return it66121_wait_ddc_ready(ctx); +} + +static int it66121_get_edid_block(void *context, u8 *buf, + unsigned int block, size_t len) +{ + struct it66121_ctx *ctx = context; + unsigned int val; + int remain = len; + int offset = 0; + int ret, cnt; + + offset = (block % 2) * len; + block = block / 2; + + ret = regmap_read(ctx->regmap, IT66121_INT_STATUS1_REG, &val); + if (ret) + return ret; + + if (val & IT66121_INT_STATUS1_DDC_BUSHANG) { + ret = it66121_abort_ddc_ops(ctx); + if (ret) + return ret; + } + + ret = it66121_clear_ddc_fifo(ctx); + if (ret) + return ret; + + while (remain > 0) { + cnt = (remain > IT66121_EDID_FIFO_SIZE) ? + IT66121_EDID_FIFO_SIZE : remain; + ret = it66121_preamble_ddc(ctx); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_DDC_COMMAND_REG, + IT66121_DDC_COMMAND_FIFO_CLR); + if (ret) + return ret; + + ret = it66121_wait_ddc_ready(ctx); + if (ret) + return ret; + + ret = regmap_read(ctx->regmap, IT66121_INT_STATUS1_REG, &val); + if (ret) + return ret; + + if (val & IT66121_INT_STATUS1_DDC_BUSHANG) { + ret = it66121_abort_ddc_ops(ctx); + if (ret) + return ret; + } + + ret = it66121_preamble_ddc(ctx); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_DDC_HEADER_REG, + IT66121_DDC_HEADER_EDID); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_DDC_OFFSET_REG, offset); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_DDC_BYTE_REG, cnt); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_DDC_SEGMENT_REG, block); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_DDC_COMMAND_REG, + IT66121_DDC_COMMAND_EDID_READ); + if (ret) + return ret; + + offset += cnt; + remain -= cnt; + + /* Per programming manual, sleep here before emptying the FIFO */ + msleep(20); + + ret = it66121_wait_ddc_ready(ctx); + if (ret) + return ret; + + do { + ret = regmap_read(ctx->regmap, IT66121_DDC_RD_FIFO_REG, &val); + if (ret) + return ret; + *(buf++) = val; + cnt--; + } while (cnt > 0); + } + + return 0; +} + +static bool it66121_is_hpd_detect(struct it66121_ctx *ctx) +{ + int val; + + if (regmap_read(ctx->regmap, IT66121_SYS_STATUS_REG, &val)) + return false; + + return val & IT66121_SYS_STATUS_HPDETECT; +} + +static int it66121_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); + int ret; + + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) + return -EINVAL; + + ret = drm_bridge_attach(bridge->encoder, ctx->next_bridge, bridge, flags); + if (ret) + return ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, + IT66121_CLK_BANK_PWROFF_RCLK, 0); + if (ret) + return ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_INT_REG, + IT66121_INT_TX_CLK_OFF, 0); + if (ret) + return ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_DRV_REG, + IT66121_AFE_DRV_PWD, 0); + if (ret) + return ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_REG, + IT66121_AFE_XP_PWDI | IT66121_AFE_XP_PWDPLL, 0); + if (ret) + return ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG, + IT66121_AFE_IP_PWDPLL, 0); + if (ret) + return ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_DRV_REG, + IT66121_AFE_DRV_RST, 0); + if (ret) + return ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_REG, + IT66121_AFE_XP_RESETB, IT66121_AFE_XP_RESETB); + if (ret) + return ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG, + IT66121_AFE_IP_RESETB, IT66121_AFE_IP_RESETB); + if (ret) + return ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_SW_RST_REG, + IT66121_SW_RST_REF, + IT66121_SW_RST_REF); + if (ret) + return ret; + + /* Per programming manual, sleep here for bridge to settle */ + msleep(50); + + /* Start interrupts */ + return regmap_write_bits(ctx->regmap, IT66121_INT_MASK1_REG, + IT66121_INT_MASK1_DDC_NOACK | + IT66121_INT_MASK1_DDC_FIFOERR | + IT66121_INT_MASK1_DDC_BUSHANG, 0); +} + +static int it66121_set_mute(struct it66121_ctx *ctx, bool mute) +{ + int ret; + unsigned int val = 0; + + if (mute) + val = IT66121_AV_MUTE_ON; + + ret = regmap_write_bits(ctx->regmap, IT66121_AV_MUTE_REG, IT66121_AV_MUTE_ON, val); + if (ret) + return ret; + + return regmap_write(ctx->regmap, IT66121_PKT_GEN_CTRL_REG, + IT66121_PKT_GEN_CTRL_ON | IT66121_PKT_GEN_CTRL_RPT); +} + +#define MAX_OUTPUT_SEL_FORMATS 1 + +static u32 *it66121_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + unsigned int *num_output_fmts) +{ + u32 *output_fmts; + + output_fmts = kcalloc(MAX_OUTPUT_SEL_FORMATS, sizeof(*output_fmts), + GFP_KERNEL); + if (!output_fmts) + return NULL; + + /* TOFIX handle more than MEDIA_BUS_FMT_RGB888_1X24 as output format */ + output_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24; + *num_output_fmts = 1; + + return output_fmts; +} + +#define MAX_INPUT_SEL_FORMATS 1 + +static u32 *it66121_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts) +{ + struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); + u32 *input_fmts; + + *num_input_fmts = 0; + + input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts), + GFP_KERNEL); + if (!input_fmts) + return NULL; + + if (ctx->bus_width == 12) + /* IT66121FN Datasheet specifies Little-Endian ordering */ + input_fmts[0] = MEDIA_BUS_FMT_RGB888_2X12_LE; + else + /* TOFIX support more input bus formats in 24bit width */ + input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24; + *num_input_fmts = 1; + + return input_fmts; +} + +static void it66121_bridge_enable(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state) +{ + struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); + struct drm_atomic_state *state = bridge_state->base.state; + + ctx->connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); + + it66121_set_mute(ctx, false); +} + +static void it66121_bridge_disable(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state) +{ + struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); + + it66121_set_mute(ctx, true); + + ctx->connector = NULL; +} + +static +void it66121_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode) +{ + int ret, i; + u8 buf[HDMI_INFOFRAME_SIZE(AVI)]; + struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); + const u16 aviinfo_reg[HDMI_AVI_INFOFRAME_SIZE] = { + IT66121_AVIINFO_DB1_REG, + IT66121_AVIINFO_DB2_REG, + IT66121_AVIINFO_DB3_REG, + IT66121_AVIINFO_DB4_REG, + IT66121_AVIINFO_DB5_REG, + IT66121_AVIINFO_DB6_REG, + IT66121_AVIINFO_DB7_REG, + IT66121_AVIINFO_DB8_REG, + IT66121_AVIINFO_DB9_REG, + IT66121_AVIINFO_DB10_REG, + IT66121_AVIINFO_DB11_REG, + IT66121_AVIINFO_DB12_REG, + IT66121_AVIINFO_DB13_REG + }; + + mutex_lock(&ctx->lock); + + hdmi_avi_infoframe_init(&ctx->hdmi_avi_infoframe); + + ret = drm_hdmi_avi_infoframe_from_display_mode(&ctx->hdmi_avi_infoframe, ctx->connector, + adjusted_mode); + if (ret) { + DRM_ERROR("Failed to setup AVI infoframe: %d\n", ret); + goto unlock; + } + + ret = hdmi_avi_infoframe_pack(&ctx->hdmi_avi_infoframe, buf, sizeof(buf)); + if (ret < 0) { + DRM_ERROR("Failed to pack infoframe: %d\n", ret); + goto unlock; + } + + /* Write new AVI infoframe packet */ + for (i = 0; i < HDMI_AVI_INFOFRAME_SIZE; i++) { + if (regmap_write(ctx->regmap, aviinfo_reg[i], buf[i + HDMI_INFOFRAME_HEADER_SIZE])) + goto unlock; + } + if (regmap_write(ctx->regmap, IT66121_AVIINFO_CSUM_REG, buf[3])) + goto unlock; + + /* Enable AVI infoframe */ + if (regmap_write(ctx->regmap, IT66121_AVI_INFO_PKT_REG, + IT66121_AVI_INFO_PKT_ON | IT66121_AVI_INFO_PKT_RPT)) + goto unlock; + + /* Set TX mode to HDMI */ + if (regmap_write(ctx->regmap, IT66121_HDMI_MODE_REG, IT66121_HDMI_MODE_HDMI)) + goto unlock; + + if (regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, + IT66121_CLK_BANK_PWROFF_TXCLK, IT66121_CLK_BANK_PWROFF_TXCLK)) + goto unlock; + + if (it66121_configure_input(ctx)) + goto unlock; + + if (it66121_configure_afe(ctx, adjusted_mode)) + goto unlock; + + regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, IT66121_CLK_BANK_PWROFF_TXCLK, 0); + +unlock: + mutex_unlock(&ctx->lock); +} + +static enum drm_mode_status it66121_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_info *info, + const struct drm_display_mode *mode) +{ + struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); + unsigned long max_clock; + + max_clock = (ctx->bus_width == 12) ? 74250 : 148500; + + if (mode->clock > max_clock) + return MODE_CLOCK_HIGH; + + if (mode->clock < 25000) + return MODE_CLOCK_LOW; + + return MODE_OK; +} + +static enum drm_connector_status it66121_bridge_detect(struct drm_bridge *bridge) +{ + struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); + + return it66121_is_hpd_detect(ctx) ? connector_status_connected + : connector_status_disconnected; +} + +static void it66121_bridge_hpd_enable(struct drm_bridge *bridge) +{ + struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); + int ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_INT_MASK1_REG, IT66121_INT_MASK1_HPD, 0); + if (ret) + dev_err(ctx->dev, "failed to enable HPD IRQ\n"); +} + +static void it66121_bridge_hpd_disable(struct drm_bridge *bridge) +{ + struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); + int ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_INT_MASK1_REG, + IT66121_INT_MASK1_HPD, IT66121_INT_MASK1_HPD); + if (ret) + dev_err(ctx->dev, "failed to disable HPD IRQ\n"); +} + +static struct edid *it66121_bridge_get_edid(struct drm_bridge *bridge, + struct drm_connector *connector) +{ + struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); + struct edid *edid; + + mutex_lock(&ctx->lock); + edid = drm_do_get_edid(connector, it66121_get_edid_block, ctx); + mutex_unlock(&ctx->lock); + + return edid; +} + +static const struct drm_bridge_funcs it66121_bridge_funcs = { + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_reset = drm_atomic_helper_bridge_reset, + .attach = it66121_bridge_attach, + .atomic_get_output_bus_fmts = it66121_bridge_atomic_get_output_bus_fmts, + .atomic_get_input_bus_fmts = it66121_bridge_atomic_get_input_bus_fmts, + .atomic_enable = it66121_bridge_enable, + .atomic_disable = it66121_bridge_disable, + .mode_set = it66121_bridge_mode_set, + .mode_valid = it66121_bridge_mode_valid, + .detect = it66121_bridge_detect, + .get_edid = it66121_bridge_get_edid, + .hpd_enable = it66121_bridge_hpd_enable, + .hpd_disable = it66121_bridge_hpd_disable, +}; + +static irqreturn_t it66121_irq_threaded_handler(int irq, void *dev_id) +{ + int ret; + unsigned int val; + struct it66121_ctx *ctx = dev_id; + struct device *dev = ctx->dev; + enum drm_connector_status status; + bool event = false; + + mutex_lock(&ctx->lock); + + ret = regmap_read(ctx->regmap, IT66121_SYS_STATUS_REG, &val); + if (ret) + goto unlock; + + if (!(val & IT66121_SYS_STATUS_ACTIVE_IRQ)) + goto unlock; + + ret = regmap_read(ctx->regmap, IT66121_INT_STATUS1_REG, &val); + if (ret) { + dev_err(dev, "Cannot read STATUS1_REG %d\n", ret); + } else { + if (val & IT66121_INT_STATUS1_DDC_FIFOERR) + it66121_clear_ddc_fifo(ctx); + if (val & (IT66121_INT_STATUS1_DDC_BUSHANG | + IT66121_INT_STATUS1_DDC_NOACK)) + it66121_abort_ddc_ops(ctx); + if (val & IT66121_INT_STATUS1_HPD_STATUS) { + regmap_write_bits(ctx->regmap, IT66121_INT_CLR1_REG, + IT66121_INT_CLR1_HPD, IT66121_INT_CLR1_HPD); + + status = it66121_is_hpd_detect(ctx) ? connector_status_connected + : connector_status_disconnected; + + event = true; + } + } + + regmap_write_bits(ctx->regmap, IT66121_SYS_STATUS_REG, + IT66121_SYS_STATUS_CLEAR_IRQ, + IT66121_SYS_STATUS_CLEAR_IRQ); + +unlock: + mutex_unlock(&ctx->lock); + + if (event) + drm_bridge_hpd_notify(&ctx->bridge, status); + + return IRQ_HANDLED; +} + +static int it661221_set_chstat(struct it66121_ctx *ctx, u8 iec60958_chstat[]) +{ + int ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_CHST_MODE_REG, iec60958_chstat[0] & 0x7C); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_CHST_CAT_REG, iec60958_chstat[1]); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_CHST_SRCNUM_REG, iec60958_chstat[2] & 0x0F); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_CHST_CHTNUM_REG, + (iec60958_chstat[2] >> 4) & 0x0F); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_CHST_CA_FS_REG, iec60958_chstat[3]); + if (ret) + return ret; + + return regmap_write(ctx->regmap, IT66121_AUD_CHST_OFS_WL_REG, iec60958_chstat[4]); +} + +static int it661221_set_lpcm_audio(struct it66121_ctx *ctx, u8 audio_src_num, u8 audio_swl) +{ + int ret; + unsigned int audio_enable = 0; + unsigned int audio_format = 0; + + switch (audio_swl) { + case 16: + audio_enable |= IT66121_AUD_16BIT; + break; + case 18: + audio_enable |= IT66121_AUD_18BIT; + break; + case 20: + audio_enable |= IT66121_AUD_20BIT; + break; + case 24: + default: + audio_enable |= IT66121_AUD_24BIT; + break; + } + + audio_format |= 0x40; + switch (audio_src_num) { + case 4: + audio_enable |= IT66121_AUD_EN_I2S3 | IT66121_AUD_EN_I2S2 | + IT66121_AUD_EN_I2S1 | IT66121_AUD_EN_I2S0; + break; + case 3: + audio_enable |= IT66121_AUD_EN_I2S2 | IT66121_AUD_EN_I2S1 | + IT66121_AUD_EN_I2S0; + break; + case 2: + audio_enable |= IT66121_AUD_EN_I2S1 | IT66121_AUD_EN_I2S0; + break; + case 1: + default: + audio_format &= ~0x40; + audio_enable |= IT66121_AUD_EN_I2S0; + break; + } + + audio_format |= 0x01; + ctx->audio.ch_enable = audio_enable; + + ret = regmap_write(ctx->regmap, IT66121_AUD_CTRL0_REG, audio_enable & 0xF0); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_CTRL1_REG, audio_format); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_FIFOMAP_REG, 0xE4); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_CTRL3_REG, 0x00); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_SRCVALID_FLAT_REG, 0x00); + if (ret) + return ret; + + return regmap_write(ctx->regmap, IT66121_AUD_HDAUDIO_REG, 0x00); +} + +static int it661221_set_ncts(struct it66121_ctx *ctx, u8 fs) +{ + int ret; + unsigned int n; + + switch (fs) { + case IT66121_AUD_FS_32K: + n = 4096; + break; + case IT66121_AUD_FS_44P1K: + n = 6272; + break; + case IT66121_AUD_FS_48K: + n = 6144; + break; + case IT66121_AUD_FS_88P2K: + n = 12544; + break; + case IT66121_AUD_FS_96K: + n = 12288; + break; + case IT66121_AUD_FS_176P4K: + n = 25088; + break; + case IT66121_AUD_FS_192K: + n = 24576; + break; + case IT66121_AUD_FS_768K: + n = 24576; + break; + default: + n = 6144; + break; + } + + ret = regmap_write(ctx->regmap, IT66121_AUD_PKT_N0_REG, (u8)((n) & 0xFF)); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_PKT_N1_REG, (u8)((n >> 8) & 0xFF)); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_PKT_N2_REG, (u8)((n >> 16) & 0xF)); + if (ret) + return ret; + + if (ctx->audio.auto_cts) { + u8 loop_cnt = 255; + u8 cts_stable_cnt = 0; + unsigned int sum_cts = 0; + unsigned int cts = 0; + unsigned int last_cts = 0; + unsigned int diff; + unsigned int val; + + while (loop_cnt--) { + msleep(30); + regmap_read(ctx->regmap, IT66121_AUD_PKT_CTS_CNT2_REG, &val); + cts = val << 12; + regmap_read(ctx->regmap, IT66121_AUD_PKT_CTS_CNT1_REG, &val); + cts |= val << 4; + regmap_read(ctx->regmap, IT66121_AUD_PKT_CTS_CNT0_REG, &val); + cts |= val >> 4; + if (cts == 0) { + continue; + } else { + if (last_cts > cts) + diff = last_cts - cts; + else + diff = cts - last_cts; + last_cts = cts; + if (diff < 5) { + cts_stable_cnt++; + sum_cts += cts; + } else { + cts_stable_cnt = 0; + sum_cts = 0; + continue; + } + + if (cts_stable_cnt >= 32) { + last_cts = (sum_cts >> 5); + break; + } + } + } + + regmap_write(ctx->regmap, IT66121_AUD_PKT_CTS0_REG, (u8)((last_cts) & 0xFF)); + regmap_write(ctx->regmap, IT66121_AUD_PKT_CTS1_REG, (u8)((last_cts >> 8) & 0xFF)); + regmap_write(ctx->regmap, IT66121_AUD_PKT_CTS2_REG, (u8)((last_cts >> 16) & 0x0F)); + } + + ret = regmap_write(ctx->regmap, 0xF8, 0xC3); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, 0xF8, 0xA5); + if (ret) + return ret; + + if (ctx->audio.auto_cts) { + ret = regmap_write_bits(ctx->regmap, IT66121_PKT_CTS_CTRL_REG, + IT66121_PKT_CTS_CTRL_SEL, + 1); + } else { + ret = regmap_write_bits(ctx->regmap, IT66121_PKT_CTS_CTRL_REG, + IT66121_PKT_CTS_CTRL_SEL, + 0); + } + + if (ret) + return ret; + + return regmap_write(ctx->regmap, 0xF8, 0xFF); +} + +static int it661221_audio_output_enable(struct it66121_ctx *ctx, bool enable) +{ + int ret; + + if (enable) { + ret = regmap_write_bits(ctx->regmap, IT66121_SW_RST_REG, + IT66121_SW_RST_AUD | IT66121_SW_RST_AREF, + 0); + if (ret) + return ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_AUD_CTRL0_REG, + IT66121_AUD_EN_I2S3 | IT66121_AUD_EN_I2S2 | + IT66121_AUD_EN_I2S1 | IT66121_AUD_EN_I2S0, + ctx->audio.ch_enable); + } else { + ret = regmap_write_bits(ctx->regmap, IT66121_AUD_CTRL0_REG, + IT66121_AUD_EN_I2S3 | IT66121_AUD_EN_I2S2 | + IT66121_AUD_EN_I2S1 | IT66121_AUD_EN_I2S0, + ctx->audio.ch_enable & 0xF0); + if (ret) + return ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_SW_RST_REG, + IT66121_SW_RST_AUD | IT66121_SW_RST_AREF, + IT66121_SW_RST_AUD | IT66121_SW_RST_AREF); + } + + return ret; +} + +static int it661221_audio_ch_enable(struct it66121_ctx *ctx, bool enable) +{ + int ret; + + if (enable) { + ret = regmap_write(ctx->regmap, IT66121_AUD_SRCVALID_FLAT_REG, 0); + if (ret) + return ret; + + ret = regmap_write(ctx->regmap, IT66121_AUD_CTRL0_REG, ctx->audio.ch_enable); + } else { + ret = regmap_write(ctx->regmap, IT66121_AUD_CTRL0_REG, ctx->audio.ch_enable & 0xF0); + } + + return ret; +} + +static int it66121_audio_hw_params(struct device *dev, void *data, + struct hdmi_codec_daifmt *daifmt, + struct hdmi_codec_params *params) +{ + u8 fs; + u8 swl; + int ret; + struct it66121_ctx *ctx = dev_get_drvdata(dev); + static u8 iec60958_chstat[5]; + unsigned int channels = params->channels; + unsigned int sample_rate = params->sample_rate; + unsigned int sample_width = params->sample_width; + + mutex_lock(&ctx->lock); + dev_dbg(dev, "%s: %u, %u, %u, %u\n", __func__, + daifmt->fmt, sample_rate, sample_width, channels); + + switch (daifmt->fmt) { + case HDMI_I2S: + dev_dbg(dev, "Using HDMI I2S\n"); + break; + default: + dev_err(dev, "Invalid or unsupported DAI format %d\n", daifmt->fmt); + ret = -EINVAL; + goto out; + } + + // Set audio clock recovery (N/CTS) + ret = regmap_write(ctx->regmap, IT66121_CLK_CTRL0_REG, + IT66121_CLK_CTRL0_AUTO_OVER_SAMPLING | + IT66121_CLK_CTRL0_EXT_MCLK_256FS | + IT66121_CLK_CTRL0_AUTO_IPCLK); + if (ret) + goto out; + + ret = regmap_write_bits(ctx->regmap, IT66121_AUD_CTRL0_REG, + IT66121_AUD_CTRL0_AUD_SEL, 0); // remove spdif selection + if (ret) + goto out; + + switch (sample_rate) { + case 44100L: + fs = IT66121_AUD_FS_44P1K; + break; + case 88200L: + fs = IT66121_AUD_FS_88P2K; + break; + case 176400L: + fs = IT66121_AUD_FS_176P4K; + break; + case 32000L: + fs = IT66121_AUD_FS_32K; + break; + case 48000L: + fs = IT66121_AUD_FS_48K; + break; + case 96000L: + fs = IT66121_AUD_FS_96K; + break; + case 192000L: + fs = IT66121_AUD_FS_192K; + break; + case 768000L: + fs = IT66121_AUD_FS_768K; + break; + default: + fs = IT66121_AUD_FS_48K; + break; + } + + ctx->audio.fs = fs; + ret = it661221_set_ncts(ctx, fs); + if (ret) { + dev_err(dev, "Failed to set N/CTS: %d\n", ret); + goto out; + } + + // Set audio format register (except audio channel enable) + ret = it661221_set_lpcm_audio(ctx, (channels + 1) / 2, sample_width); + if (ret) { + dev_err(dev, "Failed to set LPCM audio: %d\n", ret); + goto out; + } + + // Set audio channel status + iec60958_chstat[0] = 0; + if ((channels + 1) / 2 == 1) + iec60958_chstat[0] |= 0x1; + iec60958_chstat[0] &= ~(1 << 1); + iec60958_chstat[1] = 0; + iec60958_chstat[2] = (channels + 1) / 2; + iec60958_chstat[2] |= (channels << 4) & 0xF0; + iec60958_chstat[3] = fs; + + switch (sample_width) { + case 21L: + swl = IT66121_AUD_SWL_21BIT; + break; + case 24L: + swl = IT66121_AUD_SWL_24BIT; + break; + case 23L: + swl = IT66121_AUD_SWL_23BIT; + break; + case 22L: + swl = IT66121_AUD_SWL_22BIT; + break; + case 20L: + swl = IT66121_AUD_SWL_20BIT; + break; + case 17L: + swl = IT66121_AUD_SWL_17BIT; + break; + case 19L: + swl = IT66121_AUD_SWL_19BIT; + break; + case 18L: + swl = IT66121_AUD_SWL_18BIT; + break; + case 16L: + swl = IT66121_AUD_SWL_16BIT; + break; + default: + swl = IT66121_AUD_SWL_NOT_INDICATED; + break; + } + + iec60958_chstat[4] = (((~fs) << 4) & 0xF0) | swl; + ret = it661221_set_chstat(ctx, iec60958_chstat); + if (ret) { + dev_err(dev, "Failed to set channel status: %d\n", ret); + goto out; + } + + // Enable audio channel enable while input clock stable (if SPDIF). + ret = it661221_audio_ch_enable(ctx, true); + if (ret) { + dev_err(dev, "Failed to enable audio channel: %d\n", ret); + goto out; + } + + ret = regmap_write_bits(ctx->regmap, IT66121_INT_MASK1_REG, + IT66121_INT_MASK1_AUD_OVF, + 0); + if (ret) + goto out; + + dev_dbg(dev, "HDMI audio enabled.\n"); +out: + mutex_unlock(&ctx->lock); + + return ret; +} + +static int it66121_audio_startup(struct device *dev, void *data) +{ + int ret; + struct it66121_ctx *ctx = dev_get_drvdata(dev); + + dev_dbg(dev, "%s\n", __func__); + + mutex_lock(&ctx->lock); + ret = it661221_audio_output_enable(ctx, true); + if (ret) + dev_err(dev, "Failed to enable audio output: %d\n", ret); + + mutex_unlock(&ctx->lock); + + return ret; +} + +static void it66121_audio_shutdown(struct device *dev, void *data) +{ + int ret; + struct it66121_ctx *ctx = dev_get_drvdata(dev); + + dev_dbg(dev, "%s\n", __func__); + + mutex_lock(&ctx->lock); + ret = it661221_audio_output_enable(ctx, false); + if (ret) + dev_err(dev, "Failed to disable audio output: %d\n", ret); + + mutex_unlock(&ctx->lock); +} + +static int it66121_audio_mute(struct device *dev, void *data, + bool enable, int direction) +{ + int ret; + struct it66121_ctx *ctx = dev_get_drvdata(dev); + + dev_dbg(dev, "%s: enable=%s, direction=%d\n", + __func__, enable ? "true" : "false", direction); + + mutex_lock(&ctx->lock); + + if (enable) { + ret = regmap_write_bits(ctx->regmap, IT66121_AUD_SRCVALID_FLAT_REG, + IT66121_AUD_FLAT_SRC0 | IT66121_AUD_FLAT_SRC1 | + IT66121_AUD_FLAT_SRC2 | IT66121_AUD_FLAT_SRC3, + IT66121_AUD_FLAT_SRC0 | IT66121_AUD_FLAT_SRC1 | + IT66121_AUD_FLAT_SRC2 | IT66121_AUD_FLAT_SRC3); + } else { + ret = regmap_write_bits(ctx->regmap, IT66121_AUD_SRCVALID_FLAT_REG, + IT66121_AUD_FLAT_SRC0 | IT66121_AUD_FLAT_SRC1 | + IT66121_AUD_FLAT_SRC2 | IT66121_AUD_FLAT_SRC3, + 0); + } + + mutex_unlock(&ctx->lock); + + return ret; +} + +static int it66121_audio_get_eld(struct device *dev, void *data, + u8 *buf, size_t len) +{ + struct it66121_ctx *ctx = dev_get_drvdata(dev); + + mutex_lock(&ctx->lock); + + memcpy(buf, ctx->connector->eld, + min(sizeof(ctx->connector->eld), len)); + + mutex_unlock(&ctx->lock); + + return 0; +} + +static const struct hdmi_codec_ops it66121_audio_codec_ops = { + .hw_params = it66121_audio_hw_params, + .audio_startup = it66121_audio_startup, + .audio_shutdown = it66121_audio_shutdown, + .mute_stream = it66121_audio_mute, + .get_eld = it66121_audio_get_eld, + .no_capture_mute = 1, +}; + +static int it66121_audio_codec_init(struct it66121_ctx *ctx, struct device *dev) +{ + struct hdmi_codec_pdata codec_data = { + .ops = &it66121_audio_codec_ops, + .i2s = 1, /* Only i2s support for now */ + .spdif = 0, + .max_i2s_channels = 8, + }; + + dev_dbg(dev, "%s\n", __func__); + + if (!of_property_read_bool(dev->of_node, "#sound-dai-cells")) { + dev_info(dev, "No \"#sound-dai-cells\", no audio\n"); + return 0; + } + + ctx->audio.pdev = platform_device_register_data(dev, + HDMI_CODEC_DRV_NAME, + PLATFORM_DEVID_AUTO, + &codec_data, + sizeof(codec_data)); + + if (IS_ERR(ctx->audio.pdev)) { + dev_err(dev, "Failed to initialize HDMI audio codec: %d\n", + PTR_ERR_OR_ZERO(ctx->audio.pdev)); + } + + return PTR_ERR_OR_ZERO(ctx->audio.pdev); +} + +static int it66121_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + u32 revision_id, vendor_ids[2] = { 0 }, device_ids[2] = { 0 }; + struct device_node *ep; + int ret; + struct it66121_ctx *ctx; + struct device *dev = &client->dev; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(dev, "I2C check functionality failed.\n"); + return -ENXIO; + } + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ep = of_graph_get_endpoint_by_regs(dev->of_node, 0, 0); + if (!ep) + return -EINVAL; + + ctx->dev = dev; + ctx->client = client; + + of_property_read_u32(ep, "bus-width", &ctx->bus_width); + of_node_put(ep); + + if (ctx->bus_width != 12 && ctx->bus_width != 24) + return -EINVAL; + + ep = of_graph_get_remote_node(dev->of_node, 1, -1); + if (!ep) { + dev_err(ctx->dev, "The endpoint is unconnected\n"); + return -EINVAL; + } + + if (!of_device_is_available(ep)) { + of_node_put(ep); + dev_err(ctx->dev, "The remote device is disabled\n"); + return -ENODEV; + } + + ctx->next_bridge = of_drm_find_bridge(ep); + of_node_put(ep); + if (!ctx->next_bridge) { + dev_dbg(ctx->dev, "Next bridge not found, deferring probe\n"); + return -EPROBE_DEFER; + } + + i2c_set_clientdata(client, ctx); + mutex_init(&ctx->lock); + + ctx->supplies[0].supply = "vcn33"; + ctx->supplies[1].supply = "vcn18"; + ctx->supplies[2].supply = "vrf12"; + ret = devm_regulator_bulk_get(ctx->dev, 3, ctx->supplies); + if (ret) { + dev_err(ctx->dev, "regulator_bulk failed\n"); + return ret; + } + + ret = ite66121_power_on(ctx); + if (ret) + return ret; + + it66121_hw_reset(ctx); + + ctx->regmap = devm_regmap_init_i2c(client, &it66121_regmap_config); + if (IS_ERR(ctx->regmap)) { + ite66121_power_off(ctx); + return PTR_ERR(ctx->regmap); + } + + regmap_read(ctx->regmap, IT66121_VENDOR_ID0_REG, &vendor_ids[0]); + regmap_read(ctx->regmap, IT66121_VENDOR_ID1_REG, &vendor_ids[1]); + regmap_read(ctx->regmap, IT66121_DEVICE_ID0_REG, &device_ids[0]); + regmap_read(ctx->regmap, IT66121_DEVICE_ID1_REG, &device_ids[1]); + + /* Revision is shared with DEVICE_ID1 */ + revision_id = FIELD_GET(IT66121_REVISION_MASK, device_ids[1]); + device_ids[1] &= IT66121_DEVICE_ID1_MASK; + + if (vendor_ids[0] != IT66121_VENDOR_ID0 || vendor_ids[1] != IT66121_VENDOR_ID1 || + device_ids[0] != IT66121_DEVICE_ID0 || device_ids[1] != IT66121_DEVICE_ID1) { + ite66121_power_off(ctx); + return -ENODEV; + } + + ctx->bridge.funcs = &it66121_bridge_funcs; + ctx->bridge.of_node = dev->of_node; + ctx->bridge.type = DRM_MODE_CONNECTOR_HDMIA; + ctx->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD; + + ret = devm_request_threaded_irq(dev, client->irq, NULL, it66121_irq_threaded_handler, + IRQF_ONESHOT, dev_name(dev), ctx); + if (ret < 0) { + dev_err(dev, "Failed to request irq %d:%d\n", client->irq, ret); + ite66121_power_off(ctx); + return ret; + } + + it66121_audio_codec_init(ctx, dev); + + drm_bridge_add(&ctx->bridge); + + dev_info(ctx->dev, "IT66121 revision %d probed\n", revision_id); + + return 0; +} + +static int it66121_remove(struct i2c_client *client) +{ + struct it66121_ctx *ctx = i2c_get_clientdata(client); + + ite66121_power_off(ctx); + drm_bridge_remove(&ctx->bridge); + mutex_destroy(&ctx->lock); + + return 0; +} + +static const struct of_device_id it66121_dt_match[] = { + { .compatible = "ite,it66121" }, + { } +}; +MODULE_DEVICE_TABLE(of, it66121_dt_match); + +static const struct i2c_device_id it66121_id[] = { + { "it66121", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, it66121_id); + +static struct i2c_driver it66121_driver = { + .driver = { + .name = "it66121", + .of_match_table = it66121_dt_match, + }, + .probe = it66121_probe, + .remove = it66121_remove, + .id_table = it66121_id, +}; + +module_i2c_driver(it66121_driver); + +MODULE_AUTHOR("Phong LE"); +MODULE_DESCRIPTION("IT66121 HDMI transmitter driver"); +MODULE_LICENSE("GPL v2"); From da86598859411d1f930e56b32718e9efd5796d12 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Thu, 28 Jul 2022 12:56:10 -0500 Subject: [PATCH 21/65] wire up it66121 Signed-off-by: Robert Nelson --- drivers/gpu/drm/bridge/Kconfig | 8 ++++++++ drivers/gpu/drm/bridge/Makefile | 1 + 2 files changed, 9 insertions(+) diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 0f42f9e9904fa..b270414157ff0 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -51,6 +51,14 @@ config DRM_LONTIUM_LT9611 HDMI signals Please say Y if you have such hardware. +config DRM_ITE_IT66121 + tristate "ITE IT66121 HDMI bridge" + depends on OF + select DRM_KMS_HELPER + select REGMAP_I2C + help + Support for ITE IT66121 HDMI bridge. + config DRM_LVDS_CODEC tristate "Transparent LVDS encoders and decoders support" depends on OF diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index 7d50b4f34c204..d1cc5d418889c 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_DRM_TI_SN65DSI86) += ti-sn65dsi86.o obj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o obj-$(CONFIG_DRM_TI_TPD12S015) += ti-tpd12s015.o obj-$(CONFIG_DRM_NWL_MIPI_DSI) += nwl-dsi.o +obj-$(CONFIG_DRM_ITE_IT66121) += ite-it66121.o obj-y += analogix/ obj-y += cadence/ From 564512a5a0aebe39552695360a2e9d0ebf7c4e66 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Fri, 3 Feb 2023 19:27:54 -0600 Subject: [PATCH 22/65] backports: brcm80211: from: linux.git Reference: v5.10.9 Signed-off-by: Robert Nelson --- .../broadcom/brcm80211/brcmfmac/bcmsdh.c | 8 +- .../broadcom/brcm80211/brcmfmac/bus.h | 19 +- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 44 ++--- .../broadcom/brcm80211/brcmfmac/core.c | 45 +++-- .../broadcom/brcm80211/brcmfmac/dmi.c | 42 ---- .../broadcom/brcm80211/brcmfmac/firmware.c | 2 - .../broadcom/brcm80211/brcmfmac/fweh.c | 4 - .../broadcom/brcm80211/brcmfmac/pcie.c | 77 +++++--- .../broadcom/brcm80211/brcmfmac/pcie.h | 5 + .../broadcom/brcm80211/brcmfmac/pno.c | 12 +- .../broadcom/brcm80211/brcmfmac/sdio.c | 3 +- .../broadcom/brcm80211/brcmfmac/usb.c | 8 +- .../broadcom/brcm80211/brcmsmac/mac80211_if.c | 8 +- include/net/cfg80211.h | 6 +- net/mac80211/Kconfig | 2 +- net/mac80211/aead_api.c | 5 +- net/mac80211/aes_gmac.c | 5 +- net/mac80211/agg-rx.c | 5 +- net/mac80211/agg-tx.c | 24 +-- net/mac80211/airtime.c | 3 - net/mac80211/cfg.c | 36 +--- net/mac80211/chan.c | 7 +- net/mac80211/debugfs.c | 44 +++-- net/mac80211/driver-ops.c | 5 +- net/mac80211/driver-ops.h | 5 +- net/mac80211/ibss.c | 6 - net/mac80211/ieee80211_i.h | 52 ++--- net/mac80211/iface.c | 28 +-- net/mac80211/key.c | 7 - net/mac80211/key.h | 2 - net/mac80211/main.c | 28 +-- net/mac80211/mesh_hwmp.c | 2 +- net/mac80211/mesh_pathtbl.c | 7 +- net/mac80211/mesh_ps.c | 3 +- net/mac80211/mlme.c | 91 +++------ net/mac80211/rate.c | 3 +- net/mac80211/rx.c | 186 +++++------------- net/mac80211/scan.c | 54 +---- net/mac80211/spectmgmt.c | 10 +- net/mac80211/sta_info.c | 36 ++-- net/mac80211/sta_info.h | 34 +--- net/mac80211/tx.c | 101 +++------- net/mac80211/util.c | 44 ++--- net/mac80211/wme.c | 4 +- net/mac80211/wpa.c | 19 +- net/wireless/Makefile | 2 +- net/wireless/core.c | 10 +- net/wireless/core.h | 2 - net/wireless/debugfs.c | 3 +- net/wireless/mlme.c | 26 ++- net/wireless/nl80211.c | 56 ++---- net/wireless/pmsr.c | 16 +- net/wireless/reg.c | 17 +- net/wireless/scan.c | 125 ++++-------- net/wireless/sme.c | 2 +- net/wireless/util.c | 25 +-- net/wireless/wext-core.c | 5 +- net/wireless/wext-spy.c | 14 +- 58 files changed, 494 insertions(+), 950 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c index b6d0bc73923fc..f9ebb98b0e3c7 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c @@ -1217,9 +1217,13 @@ static struct sdio_driver brcmf_sdmmc_driver = { }, }; -int brcmf_sdio_register(void) +void brcmf_sdio_register(void) { - return sdio_register_driver(&brcmf_sdmmc_driver); + int ret; + + ret = sdio_register_driver(&brcmf_sdmmc_driver); + if (ret) + brcmf_err("sdio_register_driver failed: %d\n", ret); } void brcmf_sdio_exit(void) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h index 3f5da3bb6aa59..08f9d47f2e5ca 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h @@ -275,26 +275,11 @@ void brcmf_bus_add_txhdrlen(struct device *dev, uint len); #ifdef CONFIG_BRCMFMAC_SDIO void brcmf_sdio_exit(void); -int brcmf_sdio_register(void); -#else -static inline void brcmf_sdio_exit(void) { } -static inline int brcmf_sdio_register(void) { return 0; } +void brcmf_sdio_register(void); #endif - #ifdef CONFIG_BRCMFMAC_USB void brcmf_usb_exit(void); -int brcmf_usb_register(void); -#else -static inline void brcmf_usb_exit(void) { } -static inline int brcmf_usb_register(void) { return 0; } -#endif - -#ifdef CONFIG_BRCMFMAC_PCIE -void brcmf_pcie_exit(void); -int brcmf_pcie_register(void); -#else -static inline void brcmf_pcie_exit(void) { } -static inline int brcmf_pcie_register(void) { return 0; } +void brcmf_usb_register(void); #endif #endif /* BRCMFMAC_BUS_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index c2b6e5c966d04..0ee421f30aa24 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -2767,9 +2767,8 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, struct brcmf_sta_info_le sta_info_le; u32 sta_flags; u32 is_tdls_peer; - s32 total_rssi_avg = 0; - s32 total_rssi = 0; - s32 count_rssi = 0; + s32 total_rssi; + s32 count_rssi; int rssi; u32 i; @@ -2835,27 +2834,25 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES); sinfo->rx_bytes = le64_to_cpu(sta_info_le.rx_tot_bytes); } + total_rssi = 0; + count_rssi = 0; for (i = 0; i < BRCMF_ANT_MAX; i++) { - if (sta_info_le.rssi[i] == 0 || - sta_info_le.rx_lastpkt_rssi[i] == 0) - continue; - sinfo->chains |= BIT(count_rssi); - sinfo->chain_signal[count_rssi] = - sta_info_le.rx_lastpkt_rssi[i]; - sinfo->chain_signal_avg[count_rssi] = - sta_info_le.rssi[i]; - total_rssi += sta_info_le.rx_lastpkt_rssi[i]; - total_rssi_avg += sta_info_le.rssi[i]; - count_rssi++; + if (sta_info_le.rssi[i]) { + sinfo->chain_signal_avg[count_rssi] = + sta_info_le.rssi[i]; + sinfo->chain_signal[count_rssi] = + sta_info_le.rssi[i]; + total_rssi += sta_info_le.rssi[i]; + count_rssi++; + } } if (count_rssi) { - sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); - sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG); sinfo->filled |= BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL); - sinfo->filled |= - BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL_AVG); - sinfo->signal = total_rssi / count_rssi; - sinfo->signal_avg = total_rssi_avg / count_rssi; + sinfo->chains = count_rssi; + + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); + total_rssi /= count_rssi; + sinfo->signal = total_rssi; } else if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state)) { memset(&scb_val, 0, sizeof(scb_val)); @@ -5614,8 +5611,7 @@ static bool brcmf_is_linkup(struct brcmf_cfg80211_vif *vif, return false; } -static bool brcmf_is_linkdown(struct brcmf_cfg80211_vif *vif, - const struct brcmf_event_msg *e) +static bool brcmf_is_linkdown(const struct brcmf_event_msg *e) { u32 event = e->event_code; u16 flags = e->flags; @@ -5624,8 +5620,6 @@ static bool brcmf_is_linkdown(struct brcmf_cfg80211_vif *vif, (event == BRCMF_E_DISASSOC_IND) || ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) { brcmf_dbg(CONN, "Processing link down\n"); - clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state); - clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state); return true; } return false; @@ -6073,7 +6067,7 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, } else brcmf_bss_connect_done(cfg, ndev, e, true); brcmf_net_setcarrier(ifp, true); - } else if (brcmf_is_linkdown(ifp->vif, e)) { + } else if (brcmf_is_linkdown(e)) { brcmf_dbg(CONN, "Linkdown\n"); if (!brcmf_is_ibssmode(ifp->vif) && test_bit(BRCMF_VIF_STATUS_CONNECTED, diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index c8e1d505f7b5d..3dd28f5fef19e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -290,7 +290,6 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, struct brcmf_pub *drvr = ifp->drvr; struct ethhdr *eh; int head_delta; - unsigned int tx_bytes = skb->len; brcmf_dbg(DATA, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx); @@ -365,7 +364,7 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, ndev->stats.tx_dropped++; } else { ndev->stats.tx_packets++; - ndev->stats.tx_bytes += tx_bytes; + ndev->stats.tx_bytes += skb->len; } /* Return ok: we always eat the packet */ @@ -1519,34 +1518,40 @@ void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state) } } -int __init brcmf_core_init(void) +static void brcmf_driver_register(struct work_struct *work) { - int err; - - err = brcmf_sdio_register(); - if (err) - return err; +#ifdef CONFIG_BRCMFMAC_SDIO + brcmf_sdio_register(); +#endif +#ifdef CONFIG_BRCMFMAC_USB + brcmf_usb_register(); +#endif +#ifdef CONFIG_BRCMFMAC_PCIE + brcmf_pcie_register(); +#endif +} +static DECLARE_WORK(brcmf_driver_work, brcmf_driver_register); - err = brcmf_usb_register(); - if (err) - goto error_usb_register; +int __init brcmf_core_init(void) +{ + if (!schedule_work(&brcmf_driver_work)) + return -EBUSY; - err = brcmf_pcie_register(); - if (err) - goto error_pcie_register; return 0; - -error_pcie_register: - brcmf_usb_exit(); -error_usb_register: - brcmf_sdio_exit(); - return err; } void __exit brcmf_core_exit(void) { + cancel_work_sync(&brcmf_driver_work); + +#ifdef CONFIG_BRCMFMAC_SDIO brcmf_sdio_exit(); +#endif +#ifdef CONFIG_BRCMFMAC_USB brcmf_usb_exit(); +#endif +#ifdef CONFIG_BRCMFMAC_PCIE brcmf_pcie_exit(); +#endif } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/dmi.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/dmi.c index 0af452dca7664..4aa2561934d77 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/dmi.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/dmi.c @@ -40,18 +40,6 @@ static const struct brcmf_dmi_data pov_tab_p1006w_data = { BRCM_CC_43340_CHIP_ID, 2, "pov-tab-p1006w-data" }; -static const struct brcmf_dmi_data predia_basic_data = { - BRCM_CC_43341_CHIP_ID, 2, "predia-basic" -}; - -/* Note the Voyo winpad A15 tablet uses the same Ampak AP6330 module, with the - * exact same nvram file as the Prowise-PT301 tablet. Since the nvram for the - * Prowise-PT301 is already in linux-firmware we just point to that here. - */ -static const struct brcmf_dmi_data voyo_winpad_a15_data = { - BRCM_CC_4330_CHIP_ID, 4, "Prowise-PT301" -}; - static const struct dmi_system_id dmi_platform_data[] = { { /* ACEPC T8 Cherry Trail Z8350 mini PC */ @@ -75,16 +63,6 @@ static const struct dmi_system_id dmi_platform_data[] = { }, .driver_data = (void *)&acepc_t8_data, }, - { - /* Cyberbook T116 rugged tablet */ - .matches = { - DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Default string"), - DMI_EXACT_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"), - DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "20170531"), - }, - /* The factory image nvram file is identical to the ACEPC T8 one */ - .driver_data = (void *)&acepc_t8_data, - }, { /* Match for the GPDwin which unfortunately uses somewhat * generic dmi strings, which is why we test for 4 strings. @@ -133,26 +111,6 @@ static const struct dmi_system_id dmi_platform_data[] = { }, .driver_data = (void *)&pov_tab_p1006w_data, }, - { - /* Predia Basic tablet (+ with keyboard dock) */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), - DMI_MATCH(DMI_PRODUCT_NAME, "CherryTrail"), - /* Mx.WT107.KUBNGEA02 with the version-nr dropped */ - DMI_MATCH(DMI_BIOS_VERSION, "Mx.WT107.KUBNGEA"), - }, - .driver_data = (void *)&predia_basic_data, - }, - { - /* Voyo winpad A15 tablet */ - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), - DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"), - /* Above strings are too generic, also match on BIOS date */ - DMI_MATCH(DMI_BIOS_DATE, "11/20/2014"), - }, - .driver_data = (void *)&voyo_winpad_a15_data, - }, {} }; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c index a2b8d9171af2a..d821a4758f8cf 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c @@ -207,8 +207,6 @@ static int brcmf_init_nvram_parser(struct nvram_parser *nvp, size = BRCMF_FW_MAX_NVRAM_SIZE; else size = data_len; - /* Add space for properties we may add */ - size += strlen(BRCMF_FW_DEFAULT_BOARDREV) + 1; /* Alloc for extra 0 byte + roundup by 4 + length field */ size += 1 + 3 + sizeof(u32); nvp->nvram = kzalloc(size, GFP_KERNEL); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c index 1285d3685c4f5..430d2cca98b33 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c @@ -228,10 +228,6 @@ static void brcmf_fweh_event_worker(struct work_struct *work) brcmf_fweh_event_name(event->code), event->code, event->emsg.ifidx, event->emsg.bsscfgidx, event->emsg.addr); - if (event->emsg.bsscfgidx >= BRCMF_MAX_IFS) { - bphy_err(drvr, "invalid bsscfg index: %u\n", event->emsg.bsscfgidx); - goto event_free; - } /* convert event message */ emsg_be = &event->emsg; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 61febc9bfa14a..d8db0dbcfe091 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include @@ -447,6 +446,47 @@ brcmf_pcie_write_ram32(struct brcmf_pciedev_info *devinfo, u32 mem_offset, } +static void +brcmf_pcie_copy_mem_todev(struct brcmf_pciedev_info *devinfo, u32 mem_offset, + void *srcaddr, u32 len) +{ + void __iomem *address = devinfo->tcm + mem_offset; + __le32 *src32; + __le16 *src16; + u8 *src8; + + if (((ulong)address & 4) || ((ulong)srcaddr & 4) || (len & 4)) { + if (((ulong)address & 2) || ((ulong)srcaddr & 2) || (len & 2)) { + src8 = (u8 *)srcaddr; + while (len) { + iowrite8(*src8, address); + address++; + src8++; + len--; + } + } else { + len = len / 2; + src16 = (__le16 *)srcaddr; + while (len) { + iowrite16(le16_to_cpu(*src16), address); + address += 2; + src16++; + len--; + } + } + } else { + len = len / 4; + src32 = (__le32 *)srcaddr; + while (len) { + iowrite32(le32_to_cpu(*src32), address); + address += 4; + src32++; + len--; + } + } +} + + static void brcmf_pcie_copy_dev_tomem(struct brcmf_pciedev_info *devinfo, u32 mem_offset, void *dstaddr, u32 len) @@ -1306,18 +1346,6 @@ static void brcmf_pcie_down(struct device *dev) { } -static int brcmf_pcie_preinit(struct device *dev) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie; - - brcmf_dbg(PCIE, "Enter\n"); - - brcmf_pcie_intr_enable(buspub->devinfo); - brcmf_pcie_hostready(buspub->devinfo); - - return 0; -} static int brcmf_pcie_tx(struct device *dev, struct sk_buff *skb) { @@ -1426,7 +1454,6 @@ static int brcmf_pcie_reset(struct device *dev) } static const struct brcmf_bus_ops brcmf_pcie_bus_ops = { - .preinit = brcmf_pcie_preinit, .txdata = brcmf_pcie_tx, .stop = brcmf_pcie_down, .txctl = brcmf_pcie_tx_ctlpkt, @@ -1534,8 +1561,8 @@ static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo, return err; brcmf_dbg(PCIE, "Download FW %s\n", devinfo->fw_name); - memcpy_toio(devinfo->tcm + devinfo->ci->rambase, - (void *)fw->data, fw->size); + brcmf_pcie_copy_mem_todev(devinfo, devinfo->ci->rambase, + (void *)fw->data, fw->size); resetintr = get_unaligned_le32(fw->data); release_firmware(fw); @@ -1549,7 +1576,7 @@ static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo, brcmf_dbg(PCIE, "Download NVRAM %s\n", devinfo->nvram_name); address = devinfo->ci->rambase + devinfo->ci->ramsize - nvram_len; - memcpy_toio(devinfo->tcm + address, nvram, nvram_len); + brcmf_pcie_copy_mem_todev(devinfo, address, nvram, nvram_len); brcmf_fw_nvram_free(nvram); } else { brcmf_dbg(PCIE, "No matching NVRAM file found %s\n", @@ -1748,8 +1775,6 @@ static void brcmf_pcie_setup(struct device *dev, int ret, ret = brcmf_chip_get_raminfo(devinfo->ci); if (ret) { brcmf_err(bus, "Failed to get RAM info\n"); - release_firmware(fw); - brcmf_fw_nvram_free(nvram); goto fail; } @@ -1799,6 +1824,9 @@ static void brcmf_pcie_setup(struct device *dev, int ret, init_waitqueue_head(&devinfo->mbdata_resp_wait); + brcmf_pcie_intr_enable(devinfo); + brcmf_pcie_hostready(devinfo); + ret = brcmf_attach(&devinfo->pdev->dev); if (ret) goto fail; @@ -2045,7 +2073,7 @@ static int brcmf_pcie_pm_leave_D3(struct device *dev) err = brcmf_pcie_probe(pdev, NULL); if (err) - __brcmf_err(NULL, __func__, "probe after resume failed, err=%d\n", err); + brcmf_err(bus, "probe after resume failed, err=%d\n", err); return err; } @@ -2110,10 +2138,15 @@ static struct pci_driver brcmf_pciedrvr = { }; -int brcmf_pcie_register(void) +void brcmf_pcie_register(void) { + int err; + brcmf_dbg(PCIE, "Enter\n"); - return pci_register_driver(&brcmf_pciedrvr); + err = pci_register_driver(&brcmf_pciedrvr); + if (err) + brcmf_err(NULL, "PCIE driver registration failed, err=%d\n", + err); } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h index 8e6c227e8315c..d026401d20010 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h @@ -11,4 +11,9 @@ struct brcmf_pciedev { struct brcmf_pciedev_info *devinfo; }; + +void brcmf_pcie_exit(void); +void brcmf_pcie_register(void); + + #endif /* BRCMFMAC_PCIE_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c index d0a7465be586d..fabfbb0b40b0c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c @@ -158,12 +158,12 @@ static int brcmf_pno_set_random(struct brcmf_if *ifp, struct brcmf_pno_info *pi) struct brcmf_pno_macaddr_le pfn_mac; u8 *mac_addr = NULL; u8 *mac_mask = NULL; - int err, i, ri; + int err, i; - for (ri = 0; ri < pi->n_reqs; ri++) - if (pi->reqs[ri]->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { - mac_addr = pi->reqs[ri]->mac_addr; - mac_mask = pi->reqs[ri]->mac_addr_mask; + for (i = 0; i < pi->n_reqs; i++) + if (pi->reqs[i]->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { + mac_addr = pi->reqs[i]->mac_addr; + mac_mask = pi->reqs[i]->mac_addr_mask; break; } @@ -185,7 +185,7 @@ static int brcmf_pno_set_random(struct brcmf_if *ifp, struct brcmf_pno_info *pi) pfn_mac.mac[0] |= 0x02; brcmf_dbg(SCAN, "enabling random mac: reqid=%llu mac=%pM\n", - pi->reqs[ri]->reqid, pfn_mac.mac); + pi->reqs[i]->reqid, pfn_mac.mac); err = brcmf_fil_iovar_data_set(ifp, "pfn_macaddr", &pfn_mac, sizeof(pfn_mac)); if (err) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index 9929e90866f04..59c2b2b6027da 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -557,7 +557,7 @@ enum brcmf_sdio_frmtype { BRCMF_SDIO_FT_SUB, }; -#define SDIOD_DRVSTR_KEY(chip, pmu) (((unsigned int)(chip) << 16) | (pmu)) +#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu)) /* SDIO Pad drive strength to select value mappings */ struct sdiod_drive_str { @@ -4157,6 +4157,7 @@ static int brcmf_sdio_bus_reset(struct device *dev) if (ret) { brcmf_err("Failed to probe after sdio device reset: ret %d\n", ret); + brcmf_sdiod_remove(sdiodev); } return ret; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c index 9fb68c2dc7e39..586f4dfc638b9 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c @@ -1584,8 +1584,12 @@ void brcmf_usb_exit(void) usb_deregister(&brcmf_usbdrvr); } -int brcmf_usb_register(void) +void brcmf_usb_register(void) { + int ret; + brcmf_dbg(USB, "Enter\n"); - return usb_register(&brcmf_usbdrvr); + ret = usb_register(&brcmf_usbdrvr); + if (ret) + brcmf_err("usb_register failed %d\n", ret); } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c index fb76b4a69a059..818e523f6025d 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c @@ -1221,7 +1221,6 @@ static int brcms_bcma_probe(struct bcma_device *pdev) { struct brcms_info *wl; struct ieee80211_hw *hw; - int ret; dev_info(&pdev->dev, "mfg %x core %x rev %d class %d irq %d\n", pdev->id.manuf, pdev->id.id, pdev->id.rev, pdev->id.class, @@ -1246,16 +1245,11 @@ static int brcms_bcma_probe(struct bcma_device *pdev) wl = brcms_attach(pdev); if (!wl) { pr_err("%s: brcms_attach failed!\n", __func__); - ret = -ENODEV; - goto err_free_ieee80211; + return -ENODEV; } brcms_led_register(wl); return 0; - -err_free_ieee80211: - ieee80211_free_hw(hw); - return ret; } static int brcms_suspend(struct bcma_device *pdev) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 2a819be384a78..d5ab8d99739f1 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5202,6 +5202,7 @@ struct cfg80211_cqm_config; * netdev and may otherwise be used by driver read-only, will be update * by cfg80211 on change_interface * @mgmt_registrations: list of registrations for management frames + * @mgmt_registrations_lock: lock for the list * @mgmt_registrations_need_update: mgmt registrations were updated, * need to propagate the update to the driver * @mtx: mutex used to lock data in this struct, may be used by drivers @@ -5248,6 +5249,7 @@ struct wireless_dev { u32 identifier; struct list_head mgmt_registrations; + spinlock_t mgmt_registrations_lock; u8 mgmt_registrations_need_update:1; struct mutex mtx; @@ -5622,7 +5624,7 @@ unsigned int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr); */ int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr, const u8 *addr, enum nl80211_iftype iftype, - u8 data_offset, bool is_amsdu); + u8 data_offset); /** * ieee80211_data_to_8023 - convert an 802.11 data frame to 802.3 @@ -5634,7 +5636,7 @@ int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr, static inline int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, enum nl80211_iftype iftype) { - return ieee80211_data_to_8023_exthdr(skb, NULL, addr, iftype, 0, false); + return ieee80211_data_to_8023_exthdr(skb, NULL, addr, iftype, 0); } /** diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index 51ec8256b7fa9..cd9a9bd242bab 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -69,7 +69,7 @@ config MAC80211_MESH config MAC80211_LEDS bool "Enable LED triggers" depends on MAC80211 - depends on LEDS_CLASS=y || LEDS_CLASS=MAC80211 + depends on LEDS_CLASS select LEDS_TRIGGERS help This option enables a few LED triggers for different diff --git a/net/mac80211/aead_api.c b/net/mac80211/aead_api.c index b00d6f5b33f40..d7b3d905d5353 100644 --- a/net/mac80211/aead_api.c +++ b/net/mac80211/aead_api.c @@ -23,7 +23,6 @@ int aead_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, size_t aad_len, struct aead_request *aead_req; int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm); u8 *__aad; - int ret; aead_req = kzalloc(reqsize + aad_len, GFP_ATOMIC); if (!aead_req) @@ -41,10 +40,10 @@ int aead_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, size_t aad_len, aead_request_set_crypt(aead_req, sg, sg, data_len, b_0); aead_request_set_ad(aead_req, sg[0].length); - ret = crypto_aead_encrypt(aead_req); + crypto_aead_encrypt(aead_req); kfree_sensitive(aead_req); - return ret; + return 0; } int aead_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, size_t aad_len, diff --git a/net/mac80211/aes_gmac.c b/net/mac80211/aes_gmac.c index 512cab073f2e8..6f3b3a0cc10a4 100644 --- a/net/mac80211/aes_gmac.c +++ b/net/mac80211/aes_gmac.c @@ -22,7 +22,6 @@ int ieee80211_aes_gmac(struct crypto_aead *tfm, const u8 *aad, u8 *nonce, struct aead_request *aead_req; int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm); const __le16 *fc; - int ret; if (data_len < GMAC_MIC_LEN) return -EINVAL; @@ -60,10 +59,10 @@ int ieee80211_aes_gmac(struct crypto_aead *tfm, const u8 *aad, u8 *nonce, aead_request_set_crypt(aead_req, sg, sg, 0, iv); aead_request_set_ad(aead_req, GMAC_AAD_LEN + data_len); - ret = crypto_aead_encrypt(aead_req); + crypto_aead_encrypt(aead_req); kfree_sensitive(aead_req); - return ret; + return 0; } struct crypto_aead *ieee80211_aes_gmac_key_setup(const u8 key[], diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 6ef8ded4ec764..cd4cf84a7f99f 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -9,7 +9,7 @@ * Copyright 2007, Michael Wu * Copyright 2007-2010, Intel Corporation * Copyright(c) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2021 Intel Corporation + * Copyright (C) 2018-2020 Intel Corporation */ /** @@ -191,8 +191,7 @@ static void ieee80211_add_addbaext(struct ieee80211_sub_if_data *sdata, sband = ieee80211_get_sband(sdata); if (!sband) return; - he_cap = ieee80211_get_he_iftype_cap(sband, - ieee80211_vif_type_p2p(&sdata->vif)); + he_cap = ieee80211_get_he_iftype_cap(sband, sdata->vif.type); if (!he_cap) return; diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 4b4ab1961068f..b37c8a983d88d 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -9,7 +9,7 @@ * Copyright 2007, Michael Wu * Copyright 2007-2010, Intel Corporation * Copyright(c) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018 - 2022 Intel Corporation + * Copyright (C) 2018 - 2020 Intel Corporation */ #include @@ -106,7 +106,7 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata, mgmt->u.action.u.addba_req.start_seq_num = cpu_to_le16(start_seq_num << 4); - ieee80211_tx_skb_tid(sdata, skb, tid); + ieee80211_tx_skb(sdata, skb); } void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn) @@ -213,8 +213,6 @@ ieee80211_agg_start_txq(struct sta_info *sta, int tid, bool enable) struct ieee80211_txq *txq = sta->sta.txq[tid]; struct txq_info *txqi; - lockdep_assert_held(&sta->ampdu_mlme.mtx); - if (!txq) return; @@ -292,6 +290,7 @@ static void ieee80211_remove_tid_tx(struct sta_info *sta, int tid) ieee80211_assign_tid_tx(sta, tid, NULL); ieee80211_agg_splice_finish(sta->sdata, tid); + ieee80211_agg_start_txq(sta, tid, false); kfree_rcu(tid_tx, rcu_head); } @@ -481,7 +480,8 @@ static void ieee80211_send_addba_with_timeout(struct sta_info *sta, /* send AddBA request */ ieee80211_send_addba_request(sdata, sta->sta.addr, tid, - tid_tx->dialog_token, tid_tx->ssn, + tid_tx->dialog_token, + sta->tid_seq[tid] >> 4, buf_size, tid_tx->timeout); WARN_ON(test_and_set_bit(HT_AGG_STATE_SENT_ADDBA, &tid_tx->state)); @@ -523,7 +523,6 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) params.ssn = sta->tid_seq[tid] >> 4; ret = drv_ampdu_action(local, sdata, ¶ms); - tid_tx->ssn = params.ssn; if (ret == IEEE80211_AMPDU_TX_START_DELAY_ADDBA) { return; } else if (ret == IEEE80211_AMPDU_TX_START_IMMEDIATE) { @@ -626,14 +625,6 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, return -EINVAL; } - if (test_sta_flag(sta, WLAN_STA_MFP) && - !test_sta_flag(sta, WLAN_STA_AUTHORIZED)) { - ht_dbg(sdata, - "MFP STA not authorized - deny BA session request %pM tid %d\n", - sta->sta.addr, tid); - return -EINVAL; - } - /* * 802.11n-2009 11.5.1.1: If the initiating STA is an HT STA, is a * member of an IBSS, and has no other existing Block Ack agreement @@ -898,7 +889,6 @@ void ieee80211_stop_tx_ba_cb(struct sta_info *sta, int tid, { struct ieee80211_sub_if_data *sdata = sta->sdata; bool send_delba = false; - bool start_txq = false; ht_dbg(sdata, "Stopping Tx BA session for %pM tid %d\n", sta->sta.addr, tid); @@ -916,14 +906,10 @@ void ieee80211_stop_tx_ba_cb(struct sta_info *sta, int tid, send_delba = true; ieee80211_remove_tid_tx(sta, tid); - start_txq = true; unlock_sta: spin_unlock_bh(&sta->lock); - if (start_txq) - ieee80211_agg_start_txq(sta, tid, false); - if (send_delba) ieee80211_send_delba(sdata, sta->sta.addr, tid, WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE); diff --git a/net/mac80211/airtime.c b/net/mac80211/airtime.c index 758ef63669e7b..26d2f8ba70297 100644 --- a/net/mac80211/airtime.c +++ b/net/mac80211/airtime.c @@ -457,9 +457,6 @@ static u32 ieee80211_get_rate_duration(struct ieee80211_hw *hw, (status->encoding == RX_ENC_HE && streams > 8))) return 0; - if (idx >= MCS_GROUP_RATES) - return 0; - duration = airtime_mcs_groups[group].duration[idx]; duration <<= airtime_mcs_groups[group].shift; *overhead = 36 + (streams << 2); diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index c6a7f1c99abc5..7276e66ae435e 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -152,8 +152,6 @@ static int ieee80211_change_iface(struct wiphy *wiphy, struct vif_params *params) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - struct ieee80211_local *local = sdata->local; - struct sta_info *sta; int ret; ret = ieee80211_if_change_type(sdata, type); @@ -164,24 +162,7 @@ static int ieee80211_change_iface(struct wiphy *wiphy, RCU_INIT_POINTER(sdata->u.vlan.sta, NULL); ieee80211_check_fast_rx_iface(sdata); } else if (type == NL80211_IFTYPE_STATION && params->use_4addr >= 0) { - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - - if (params->use_4addr == ifmgd->use_4addr) - return 0; - sdata->u.mgd.use_4addr = params->use_4addr; - if (!ifmgd->associated) - return 0; - - mutex_lock(&local->sta_mtx); - sta = sta_info_get(sdata, ifmgd->bssid); - if (sta) - drv_sta_set_4addr(local, sdata, &sta->sta, - params->use_4addr); - mutex_unlock(&local->sta_mtx); - - if (params->use_4addr) - ieee80211_send_4addr_nullfunc(local, sdata); } if (sdata->vif.type == NL80211_IFTYPE_MONITOR) { @@ -1217,10 +1198,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, return 0; error: - mutex_lock(&local->mtx); ieee80211_vif_release_channel(sdata); - mutex_unlock(&local->mtx); - return err; } @@ -1811,10 +1789,8 @@ static int ieee80211_change_station(struct wiphy *wiphy, } if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && - sta->sdata->u.vlan.sta) { - ieee80211_clear_fast_rx(sta); + sta->sdata->u.vlan.sta) RCU_INIT_POINTER(sta->sdata->u.vlan.sta, NULL); - } if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) ieee80211_vif_dec_num_mcast(sta->sdata); @@ -2076,12 +2052,14 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh, const struct mesh_setup *setup) { u8 *new_ie; + const u8 *old_ie; struct ieee80211_sub_if_data *sdata = container_of(ifmsh, struct ieee80211_sub_if_data, u.mesh); int i; /* allocate information elements */ new_ie = NULL; + old_ie = ifmsh->ie; if (setup->ie_len) { new_ie = kmemdup(setup->ie, setup->ie_len, @@ -2091,6 +2069,7 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh, } ifmsh->ie_len = setup->ie_len; ifmsh->ie = new_ie; + kfree(old_ie); /* now copy the rest of the setup parameters */ ifmsh->mesh_id_len = setup->mesh_id_len; @@ -2982,14 +2961,14 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy, continue; for (j = 0; j < IEEE80211_HT_MCS_MASK_LEN; j++) { - if (sdata->rc_rateidx_mcs_mask[i][j] != 0xff) { + if (~sdata->rc_rateidx_mcs_mask[i][j]) { sdata->rc_has_mcs_mask[i] = true; break; } } for (j = 0; j < NL80211_VHT_NSS_MAX; j++) { - if (sdata->rc_rateidx_vht_mcs_mask[i][j] != 0xffff) { + if (~sdata->rc_rateidx_vht_mcs_mask[i][j]) { sdata->rc_has_vht_mcs_mask[i] = true; break; } @@ -3357,6 +3336,9 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata, case NL80211_IFTYPE_MESH_POINT: { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + if (params->chandef.width != sdata->vif.bss_conf.chandef.width) + return -EINVAL; + /* changes into another band are not supported */ if (sdata->vif.bss_conf.chandef.chan->band != params->chandef.chan->band) diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 5639a71257e41..8f48aff74c7ba 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -1652,9 +1652,12 @@ int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata) if (new_ctx->replace_state == IEEE80211_CHANCTX_REPLACE_NONE) { if (old_ctx) - return ieee80211_vif_use_reserved_reassign(sdata); + err = ieee80211_vif_use_reserved_reassign(sdata); + else + err = ieee80211_vif_use_reserved_assign(sdata); - return ieee80211_vif_use_reserved_assign(sdata); + if (err) + return err; } /* diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index de5cd3818690c..90470392fdaa7 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -120,17 +120,18 @@ static ssize_t aqm_write(struct file *file, { struct ieee80211_local *local = file->private_data; char buf[100]; + size_t len; - if (count >= sizeof(buf)) + if (count > sizeof(buf)) return -EINVAL; if (copy_from_user(buf, user_buf, count)) return -EFAULT; - if (count && buf[count - 1] == '\n') - buf[count - 1] = '\0'; - else - buf[count] = '\0'; + buf[sizeof(buf) - 1] = '\0'; + len = strlen(buf); + if (len > 0 && buf[len-1] == '\n') + buf[len-1] = 0; if (sscanf(buf, "fq_limit %u", &local->fq.limit) == 1) return count; @@ -176,17 +177,18 @@ static ssize_t airtime_flags_write(struct file *file, { struct ieee80211_local *local = file->private_data; char buf[16]; + size_t len; - if (count >= sizeof(buf)) + if (count > sizeof(buf)) return -EINVAL; if (copy_from_user(buf, user_buf, count)) return -EFAULT; - if (count && buf[count - 1] == '\n') - buf[count - 1] = '\0'; - else - buf[count] = '\0'; + buf[sizeof(buf) - 1] = 0; + len = strlen(buf); + if (len > 0 && buf[len - 1] == '\n') + buf[len - 1] = 0; if (kstrtou16(buf, 0, &local->airtime_flags)) return -EINVAL; @@ -235,19 +237,20 @@ static ssize_t aql_txq_limit_write(struct file *file, { struct ieee80211_local *local = file->private_data; char buf[100]; + size_t len; u32 ac, q_limit_low, q_limit_high, q_limit_low_old, q_limit_high_old; struct sta_info *sta; - if (count >= sizeof(buf)) + if (count > sizeof(buf)) return -EINVAL; if (copy_from_user(buf, user_buf, count)) return -EFAULT; - if (count && buf[count - 1] == '\n') - buf[count - 1] = '\0'; - else - buf[count] = '\0'; + buf[sizeof(buf) - 1] = 0; + len = strlen(buf); + if (len > 0 && buf[len - 1] == '\n') + buf[len - 1] = 0; if (sscanf(buf, "%u %u %u", &ac, &q_limit_low, &q_limit_high) != 3) return -EINVAL; @@ -303,17 +306,18 @@ static ssize_t force_tx_status_write(struct file *file, { struct ieee80211_local *local = file->private_data; char buf[3]; + size_t len; - if (count >= sizeof(buf)) + if (count > sizeof(buf)) return -EINVAL; if (copy_from_user(buf, user_buf, count)) return -EFAULT; - if (count && buf[count - 1] == '\n') - buf[count - 1] = '\0'; - else - buf[count] = '\0'; + buf[sizeof(buf) - 1] = '\0'; + len = strlen(buf); + if (len > 0 && buf[len - 1] == '\n') + buf[len - 1] = 0; if (buf[0] == '0' && buf[1] == '\0') local->force_tx_status = 0; diff --git a/net/mac80211/driver-ops.c b/net/mac80211/driver-ops.c index 48322e45e7ddb..c9a8a2433e8ac 100644 --- a/net/mac80211/driver-ops.c +++ b/net/mac80211/driver-ops.c @@ -125,11 +125,8 @@ int drv_sta_state(struct ieee80211_local *local, } else if (old_state == IEEE80211_STA_AUTH && new_state == IEEE80211_STA_ASSOC) { ret = drv_sta_add(local, sdata, &sta->sta); - if (ret == 0) { + if (ret == 0) sta->uploaded = true; - if (rcu_access_pointer(sta->sta.rates)) - drv_sta_rate_tbl_update(local, sdata, &sta->sta); - } } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTH) { drv_sta_remove(local, sdata, &sta->sta); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index a172f69c71123..bcdfd19a596be 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -1201,11 +1201,8 @@ static inline void drv_wake_tx_queue(struct ieee80211_local *local, { struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->txq.vif); - /* In reconfig don't transmit now, but mark for waking later */ - if (local->in_reconfig) { - set_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txq->flags); + if (local->in_reconfig) return; - } if (!check_sdata_in_driver(sdata)) return; diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 78ae58ec397a0..1f552f374e97d 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -541,10 +541,6 @@ int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata) sdata_assert_lock(sdata); - /* When not connected/joined, sending CSA doesn't make sense. */ - if (ifibss->state != IEEE80211_IBSS_MLME_JOINED) - return -ENOLINK; - /* update cfg80211 bss information with the new channel */ if (!is_zero_ether_addr(ifibss->bssid)) { cbss = cfg80211_get_bss(sdata->local->hw.wiphy, @@ -1878,8 +1874,6 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) /* remove beacon */ kfree(sdata->u.ibss.ie); - sdata->u.ibss.ie = NULL; - sdata->u.ibss.ie_len = 0; /* on the next join, re-program HT parameters */ memset(&ifibss->ht_capa, 0, sizeof(ifibss->ht_capa)); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 63499db5c63d9..2a21226fb518a 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -50,6 +50,12 @@ struct ieee80211_local; #define IEEE80211_ENCRYPT_HEADROOM 8 #define IEEE80211_ENCRYPT_TAILROOM 18 +/* IEEE 802.11 (Ch. 9.5 Defragmentation) requires support for concurrent + * reception of at least three fragmented frames. This limit can be increased + * by changing this define, at the cost of slower frame reassembly and + * increased memory use (about 2 kB of RAM per entry). */ +#define IEEE80211_FRAGMENT_MAX 4 + /* power level hasn't been configured (or set to automatic) */ #define IEEE80211_UNSET_POWER_LEVEL INT_MIN @@ -82,6 +88,18 @@ extern const u8 ieee80211_ac_to_qos_mask[IEEE80211_NUM_ACS]; #define IEEE80211_MAX_NAN_INSTANCE_ID 255 +struct ieee80211_fragment_entry { + struct sk_buff_head skb_list; + unsigned long first_frag_time; + u16 seq; + u16 extra_len; + u16 last_frag; + u8 rx_queue; + bool check_sequential_pn; /* needed for CCMP/GCMP */ + u8 last_pn[6]; /* PN of the last fragment if CCMP was used */ +}; + + struct ieee80211_bss { u32 device_ts_beacon, device_ts_presp; @@ -223,15 +241,8 @@ struct ieee80211_rx_data { */ int security_idx; - union { - struct { - u32 iv32; - u16 iv16; - } tkip; - struct { - u8 pn[IEEE80211_CCMP_PN_LEN]; - } ccm_gcm; - }; + u32 tkip_iv32; + u16 tkip_iv16; }; struct ieee80211_csa_settings { @@ -374,7 +385,7 @@ struct ieee80211_mgd_auth_data { u8 key[WLAN_KEY_LEN_WEP104]; u8 key_len, key_idx; - bool done, waiting; + bool done; bool peer_confirmed; bool timeout_started; @@ -895,7 +906,9 @@ struct ieee80211_sub_if_data { char name[IFNAMSIZ]; - struct ieee80211_fragment_cache frags; + /* Fragment table for host-based reassembly */ + struct ieee80211_fragment_entry fragments[IEEE80211_FRAGMENT_MAX]; + unsigned int fragment_next; /* TID bitmap for NoAck policy */ u16 noack_map; @@ -1069,7 +1082,6 @@ enum queue_stop_reason { IEEE80211_QUEUE_STOP_REASON_FLUSH, IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN, IEEE80211_QUEUE_STOP_REASON_RESERVE_TID, - IEEE80211_QUEUE_STOP_REASON_IFTYPE_CHANGE, IEEE80211_QUEUE_STOP_REASONS, }; @@ -1103,9 +1115,6 @@ struct tpt_led_trigger { * a scan complete for an aborted scan. * @SCAN_HW_CANCELLED: Set for our scan work function when the scan is being * cancelled. - * @SCAN_BEACON_WAIT: Set whenever we're passive scanning because of radar/no-IR - * and could send a probe request after receiving a beacon. - * @SCAN_BEACON_DONE: Beacon received, we can now send a probe request */ enum { SCAN_SW_SCANNING, @@ -1114,8 +1123,6 @@ enum { SCAN_COMPLETED, SCAN_ABORTED, SCAN_HW_CANCELLED, - SCAN_BEACON_WAIT, - SCAN_BEACON_DONE, }; /** @@ -1450,7 +1457,7 @@ ieee80211_get_sband(struct ieee80211_sub_if_data *sdata) rcu_read_lock(); chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); - if (!chanctx_conf) { + if (WARN_ON_ONCE(!chanctx_conf)) { rcu_read_unlock(); return NULL; } @@ -1485,6 +1492,7 @@ struct ieee802_11_elems { const u8 *supp_rates; const u8 *ds_params; const struct ieee80211_tim_ie *tim; + const u8 *challenge; const u8 *rsn; const u8 *rsnx; const u8 *erp_info; @@ -1537,6 +1545,7 @@ struct ieee802_11_elems { u8 ssid_len; u8 supp_rates_len; u8 tim_len; + u8 challenge_len; u8 rsn_len; u8 rsnx_len; u8 ext_supp_rates_len; @@ -1551,8 +1560,6 @@ struct ieee802_11_elems { u8 country_elem_len; u8 bssid_index_len; - void *nontx_profile; - /* whether a parse error occurred while retrieving these elements */ bool parse_error; }; @@ -2056,8 +2063,6 @@ void ieee80211_dynamic_ps_timer(struct timer_list *t); void ieee80211_send_nullfunc(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, bool powersave); -void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata); void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata, struct ieee80211_hdr *hdr, bool ack, u16 tx_time); @@ -2321,7 +2326,4 @@ u32 ieee80211_calc_expected_tx_airtime(struct ieee80211_hw *hw, #define debug_noinline #endif -void ieee80211_init_frag_cache(struct ieee80211_fragment_cache *cache); -void ieee80211_destroy_frag_cache(struct ieee80211_fragment_cache *cache); - #endif /* IEEE80211_I_H */ diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 3a15ef8dd3228..44154cc596cd4 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -8,7 +8,7 @@ * Copyright 2008, Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright (c) 2016 Intel Deutschland GmbH - * Copyright (C) 2018-2021 Intel Corporation + * Copyright (C) 2018-2020 Intel Corporation */ #include #include @@ -679,12 +679,16 @@ static void ieee80211_set_multicast_list(struct net_device *dev) */ static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata) { + int i; + /* free extra data */ ieee80211_free_keys(sdata, false); ieee80211_debugfs_remove_netdev(sdata); - ieee80211_destroy_frag_cache(&sdata->frags); + for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) + __skb_queue_purge(&sdata->fragments[i].skb_list); + sdata->fragment_next = 0; if (ieee80211_vif_is_mesh(&sdata->vif)) ieee80211_mesh_teardown_sdata(sdata); @@ -1650,10 +1654,6 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata, if (ret) return ret; - ieee80211_stop_vif_queues(local, sdata, - IEEE80211_QUEUE_STOP_REASON_IFTYPE_CHANGE); - synchronize_net(); - ieee80211_do_stop(sdata, false); ieee80211_teardown_sdata(sdata); @@ -1676,8 +1676,6 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata, err = ieee80211_do_open(&sdata->wdev, false); WARN(err, "type change: do_open returned %d", err); - ieee80211_wake_vif_queues(local, sdata, - IEEE80211_QUEUE_STOP_REASON_IFTYPE_CHANGE); return ret; } @@ -1946,7 +1944,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, sdata->wdev.wiphy = local->hw.wiphy; sdata->local = local; - ieee80211_init_frag_cache(&sdata->frags); + for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) + skb_queue_head_init(&sdata->fragments[i].skb_list); INIT_LIST_HEAD(&sdata->key_list); @@ -2000,16 +1999,9 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, netdev_set_default_ethtool_ops(ndev, &ieee80211_ethtool_ops); - /* MTU range is normally 256 - 2304, where the upper limit is - * the maximum MSDU size. Monitor interfaces send and receive - * MPDU and A-MSDU frames which may be much larger so we do - * not impose an upper limit in that case. - */ + /* MTU range: 256 - 2304 */ ndev->min_mtu = 256; - if (type == NL80211_IFTYPE_MONITOR) - ndev->max_mtu = 0; - else - ndev->max_mtu = local->hw.max_mtu; + ndev->max_mtu = local->hw.max_mtu; ret = register_netdevice(ndev); if (ret) { diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 6a72c33679ba9..8c5f829ff6d71 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -799,7 +799,6 @@ int ieee80211_key_link(struct ieee80211_key *key, struct ieee80211_sub_if_data *sdata, struct sta_info *sta) { - static atomic_t key_color = ATOMIC_INIT(0); struct ieee80211_key *old_key; int idx = key->conf.keyidx; bool pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE; @@ -851,12 +850,6 @@ int ieee80211_key_link(struct ieee80211_key *key, key->sdata = sdata; key->sta = sta; - /* - * Assign a unique ID to every key so we can easily prevent mixed - * key and fragment cache attacks. - */ - key->color = atomic_inc_return(&key_color); - increment_tailroom_need_count(sdata); ret = ieee80211_key_replace(sdata, sta, pairwise, old_key, key); diff --git a/net/mac80211/key.h b/net/mac80211/key.h index 1e326c89d7217..7ad72e9b4991d 100644 --- a/net/mac80211/key.h +++ b/net/mac80211/key.h @@ -128,8 +128,6 @@ struct ieee80211_key { } debugfs; #endif - unsigned int color; - /* * key config, must be last because it contains key * material as variable length member diff --git a/net/mac80211/main.c b/net/mac80211/main.c index ae90ac3be59aa..523380aed92eb 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -982,19 +982,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) continue; if (!dflt_chandef.chan) { - /* - * Assign the first enabled channel to dflt_chandef - * from the list of channels - */ - for (i = 0; i < sband->n_channels; i++) - if (!(sband->channels[i].flags & - IEEE80211_CHAN_DISABLED)) - break; - /* if none found then use the first anyway */ - if (i == sband->n_channels) - i = 0; cfg80211_chandef_create(&dflt_chandef, - &sband->channels[i], + &sband->channels[0], NL80211_CHAN_NO_HT); /* init channel we're on */ if (!local->use_chanctx && !local->_oper_chandef.chan) { @@ -1150,11 +1139,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if (local->hw.wiphy->max_scan_ie_len) local->hw.wiphy->max_scan_ie_len -= local->scan_ies_len; - if (WARN_ON(!ieee80211_cs_list_valid(local->hw.cipher_schemes, - local->hw.n_cipher_schemes))) { - result = -EINVAL; - goto fail_workqueue; - } + WARN_ON(!ieee80211_cs_list_valid(local->hw.cipher_schemes, + local->hw.n_cipher_schemes)); result = ieee80211_init_cipher_suites(local); if (result < 0) @@ -1349,10 +1335,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) ieee80211_led_exit(local); destroy_workqueue(local->workqueue); fail_workqueue: - if (local->wiphy_ciphers_allocated) { + if (local->wiphy_ciphers_allocated) kfree(local->hw.wiphy->cipher_suites); - local->wiphy_ciphers_allocated = false; - } kfree(local->int_scan_req); return result; } @@ -1422,10 +1406,8 @@ void ieee80211_free_hw(struct ieee80211_hw *hw) mutex_destroy(&local->iflist_mtx); mutex_destroy(&local->mtx); - if (local->wiphy_ciphers_allocated) { + if (local->wiphy_ciphers_allocated) kfree(local->hw.wiphy->cipher_suites); - local->wiphy_ciphers_allocated = false; - } idr_for_each(&local->ack_status_frames, ieee80211_free_ack_frame, NULL); diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 3db514c4c63ab..313eee12410ec 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -356,7 +356,7 @@ u32 airtime_link_metric_get(struct ieee80211_local *local, */ tx_time = (device_constant + 10 * test_frame_len / rate); estimated_retx = ((1 << (2 * ARITH_SHIFT)) / (s_unit - err)); - result = ((u64)tx_time * estimated_retx) >> (2 * ARITH_SHIFT); + result = (tx_time * estimated_retx) >> (2 * ARITH_SHIFT); return (u32)result; } diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index c2b051e0610ab..620ecf922408b 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -60,10 +60,7 @@ static struct mesh_table *mesh_table_alloc(void) atomic_set(&newtbl->entries, 0); spin_lock_init(&newtbl->gates_lock); spin_lock_init(&newtbl->walk_lock); - if (rhashtable_init(&newtbl->rhead, &mesh_rht_params)) { - kfree(newtbl); - return NULL; - } + rhashtable_init(&newtbl->rhead, &mesh_rht_params); return newtbl; } @@ -718,7 +715,7 @@ int mesh_path_send_to_gates(struct mesh_path *mpath) void mesh_path_discard_frame(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { - ieee80211_free_txskb(&sdata->local->hw, skb); + kfree_skb(skb); sdata->u.mesh.mshstats.dropped_frames_no_route++; } diff --git a/net/mac80211/mesh_ps.c b/net/mac80211/mesh_ps.c index 3fbd0b9ff9135..204830a55240b 100644 --- a/net/mac80211/mesh_ps.c +++ b/net/mac80211/mesh_ps.c @@ -2,7 +2,6 @@ /* * Copyright 2012-2013, Marco Porsch * Copyright 2012-2013, cozybit Inc. - * Copyright (C) 2021 Intel Corporation */ #include "mesh.h" @@ -589,7 +588,7 @@ void ieee80211_mps_frame_release(struct sta_info *sta, /* only transmit to PS STA with announced, non-zero awake window */ if (test_sta_flag(sta, WLAN_STA_PS_STA) && - (!elems->awake_window || !get_unaligned_le16(elems->awake_window))) + (!elems->awake_window || !le16_to_cpu(*elems->awake_window))) return; if (!test_sta_flag(sta, WLAN_STA_MPSP_OWNER)) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index c52b8eb7fb8a2..6adfcb9c06dcc 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -37,7 +37,6 @@ #define IEEE80211_AUTH_TIMEOUT_SAE (HZ * 2) #define IEEE80211_AUTH_MAX_TRIES 3 #define IEEE80211_AUTH_WAIT_ASSOC (HZ * 5) -#define IEEE80211_AUTH_WAIT_SAE_RETRY (HZ * 2) #define IEEE80211_ASSOC_TIMEOUT (HZ / 5) #define IEEE80211_ASSOC_TIMEOUT_LONG (HZ / 2) #define IEEE80211_ASSOC_TIMEOUT_SHORT (HZ / 10) @@ -1095,6 +1094,11 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local, struct ieee80211_hdr_3addr *nullfunc; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + /* Don't send NDPs when STA is connected HE */ + if (sdata->vif.type == NL80211_IFTYPE_STATION && + !(ifmgd->flags & IEEE80211_STA_DISABLE_HE)) + return; + skb = ieee80211_nullfunc_get(&local->hw, &sdata->vif, !ieee80211_hw_check(&local->hw, DOESNT_SUPPORT_QOS_NDP)); if (!skb) @@ -1116,8 +1120,8 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local, ieee80211_tx_skb(sdata, skb); } -void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata) +static void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) { struct sk_buff *skb; struct ieee80211_hdr *nullfunc; @@ -1126,6 +1130,10 @@ void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local, if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) return; + /* Don't send NDPs when connected HE */ + if (!(sdata->u.mgd.flags & IEEE80211_STA_DISABLE_HE)) + return; + skb = dev_alloc_skb(local->hw.extra_tx_headroom + 30); if (!skb) return; @@ -1287,11 +1295,6 @@ static void ieee80211_chswitch_post_beacon(struct ieee80211_sub_if_data *sdata) sdata->vif.csa_active = false; ifmgd->csa_waiting_bcn = false; - /* - * If the CSA IE is still present on the beacon after the switch, - * we need to consider it as a new CSA (possibly to self). - */ - ifmgd->beacon_crc_valid = false; ret = drv_post_channel_switch(sdata); if (ret) { @@ -2494,18 +2497,11 @@ static void ieee80211_sta_tx_wmm_ac_notify(struct ieee80211_sub_if_data *sdata, u16 tx_time) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - u16 tid; - int ac; - struct ieee80211_sta_tx_tspec *tx_tspec; + u16 tid = ieee80211_get_tid(hdr); + int ac = ieee80211_ac_from_tid(tid); + struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac]; unsigned long now = jiffies; - if (!ieee80211_is_data_qos(hdr->frame_control)) - return; - - tid = ieee80211_get_tid(hdr); - ac = ieee80211_ac_from_tid(tid); - tx_tspec = &ifmgd->tx_tspec[ac]; - if (likely(!tx_tspec->admitted_time)) return; @@ -2899,14 +2895,14 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata, { struct ieee80211_local *local = sdata->local; struct ieee80211_mgd_auth_data *auth_data = sdata->u.mgd.auth_data; - const struct element *challenge; u8 *pos; + struct ieee802_11_elems elems; u32 tx_flags = 0; pos = mgmt->u.auth.variable; - challenge = cfg80211_find_elem(WLAN_EID_CHALLENGE, pos, - len - (pos - (u8 *)mgmt)); - if (!challenge) + ieee802_11_parse_elems(pos, len - (pos - (u8 *)mgmt), false, &elems, + mgmt->bssid, auth_data->bss->bssid); + if (!elems.challenge) return; auth_data->expected_transaction = 4; drv_mgd_prepare_tx(sdata->local, sdata, 0); @@ -2914,8 +2910,7 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata, tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS | IEEE80211_TX_INTFL_MLME_CONN_TX; ieee80211_send_auth(sdata, 3, auth_data->algorithm, 0, - (void *)challenge, - challenge->datalen + sizeof(*challenge), + elems.challenge - 2, elems.challenge_len + 2, auth_data->bss->bssid, auth_data->bss->bssid, auth_data->key, auth_data->key_len, auth_data->key_idx, tx_flags); @@ -3001,15 +2996,8 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, (status_code == WLAN_STATUS_ANTI_CLOG_REQUIRED || (auth_transaction == 1 && (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT || - status_code == WLAN_STATUS_SAE_PK)))) { - /* waiting for userspace now */ - ifmgd->auth_data->waiting = true; - ifmgd->auth_data->timeout = - jiffies + IEEE80211_AUTH_WAIT_SAE_RETRY; - ifmgd->auth_data->timeout_started = true; - run_again(sdata, ifmgd->auth_data->timeout); + status_code == WLAN_STATUS_SAE_PK)))) return; - } sdata_info(sdata, "%pM denied authentication (status %d)\n", mgmt->sa, status_code); @@ -3300,7 +3288,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, } capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info); ieee802_11_parse_elems(pos, len - (pos - (u8 *)mgmt), false, elems, - mgmt->bssid, NULL); + mgmt->bssid, assoc_data->bss->bssid); if (elems->aid_resp) aid = le16_to_cpu(elems->aid_resp->aid); @@ -3394,7 +3382,6 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, sdata_info(sdata, "AP bug: VHT operation missing from AssocResp\n"); } - kfree(bss_elems.nontx_profile); } /* @@ -3530,12 +3517,6 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, cbss->transmitted_bss->bssid); bss_conf->bssid_indicator = cbss->max_bssid_indicator; bss_conf->bssid_index = cbss->bssid_index; - } else { - bss_conf->nontransmitted = false; - memset(bss_conf->transmitter_bssid, 0, - sizeof(bss_conf->transmitter_bssid)); - bss_conf->bssid_indicator = 0; - bss_conf->bssid_index = 0; } /* @@ -3709,7 +3690,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, return; ieee802_11_parse_elems(pos, len - (pos - (u8 *)mgmt), false, &elems, - mgmt->bssid, NULL); + mgmt->bssid, assoc_data->bss->bssid); if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY && elems.timeout_int && @@ -4033,20 +4014,15 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, if (elems.mbssid_config_ie) bss_conf->profile_periodicity = elems.mbssid_config_ie->profile_periodicity; - else - bss_conf->profile_periodicity = 0; if (elems.ext_capab_len >= 11 && (elems.ext_capab[10] & WLAN_EXT_CAPA11_EMA_SUPPORT)) bss_conf->ema_ap = true; - else - bss_conf->ema_ap = false; /* continue assoc process */ ifmgd->assoc_data->timeout = jiffies; ifmgd->assoc_data->timeout_started = true; run_again(sdata, ifmgd->assoc_data->timeout); - kfree(elems.nontx_profile); return; } @@ -4224,7 +4200,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ieee80211_report_disconnect(sdata, deauth_buf, sizeof(deauth_buf), true, WLAN_REASON_DEAUTH_LEAVING); - goto free; + return; } if (sta && elems.opmode_notif) @@ -4239,8 +4215,6 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, elems.cisco_dtpc_elem); ieee80211_bss_info_change_notify(sdata, changed); -free: - kfree(elems.nontx_profile); } void ieee80211_sta_rx_queued_ext(struct ieee80211_sub_if_data *sdata, @@ -4545,10 +4519,10 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) if (ifmgd->auth_data && ifmgd->auth_data->timeout_started && time_after(jiffies, ifmgd->auth_data->timeout)) { - if (ifmgd->auth_data->done || ifmgd->auth_data->waiting) { + if (ifmgd->auth_data->done) { /* - * ok ... we waited for assoc or continuation but - * userspace didn't do it, so kill the auth data + * ok ... we waited for assoc but userspace didn't, + * so let's just kill the auth data */ ieee80211_destroy_auth_data(sdata, false); } else if (ieee80211_auth(sdata)) { @@ -4686,10 +4660,7 @@ static void ieee80211_sta_conn_mon_timer(struct timer_list *t) timeout = sta->rx_stats.last_rx; timeout += IEEE80211_CONNECTION_IDLE_TIME; - /* If timeout is after now, then update timer to fire at - * the later date, but do not actually probe at this time. - */ - if (time_is_after_jiffies(timeout)) { + if (time_is_before_jiffies(timeout)) { mod_timer(&ifmgd->conn_mon_timer, round_jiffies_up(timeout)); return; } @@ -5052,7 +5023,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, he_oper_ie = cfg80211_find_ext_ie(WLAN_EID_EXT_HE_OPERATION, ies->data, ies->len); if (he_oper_ie && - he_oper_ie[1] >= ieee80211_he_oper_size(&he_oper_ie[3])) + he_oper_ie[1] == ieee80211_he_oper_size(&he_oper_ie[3])) he_oper = (void *)(he_oper_ie + 3); else he_oper = NULL; @@ -5213,7 +5184,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, */ if (new_sta) { u32 rates = 0, basic_rates = 0; - bool have_higher_than_11mbit = false; + bool have_higher_than_11mbit; int min_rate = INT_MAX, min_rate_index = -1; const struct cfg80211_bss_ies *ies; int shift = ieee80211_vif_get_shift(&sdata->vif); @@ -5770,16 +5741,12 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, beacon_ies->data, beacon_ies->len); if (elem && elem->datalen >= 3) sdata->vif.bss_conf.profile_periodicity = elem->data[2]; - else - sdata->vif.bss_conf.profile_periodicity = 0; elem = cfg80211_find_elem(WLAN_EID_EXT_CAPABILITY, beacon_ies->data, beacon_ies->len); if (elem && elem->datalen >= 11 && (elem->data[10] & WLAN_EXT_CAPA11_EMA_SUPPORT)) sdata->vif.bss_conf.ema_ap = true; - else - sdata->vif.bss_conf.ema_ap = false; } else { assoc_data->timeout = jiffies; assoc_data->timeout_started = true; diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 63652c39c8e07..45927202c71c6 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -960,8 +960,7 @@ int rate_control_set_rates(struct ieee80211_hw *hw, if (old) kfree_rcu(old, rcu_head); - if (sta->uploaded) - drv_sta_rate_tbl_update(hw_to_local(hw), sta->sdata, pubsta); + drv_sta_rate_tbl_update(hw_to_local(hw), sta->sdata, pubsta); ieee80211_sta_set_expected_throughput(pubsta, sta_get_expected_throughput(sta)); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 97a63b940482d..2a5a11f92b03e 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -6,7 +6,7 @@ * Copyright 2007-2010 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright(c) 2015 - 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2021 Intel Corporation + * Copyright (C) 2018-2020 Intel Corporation */ #include @@ -1387,7 +1387,8 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx, goto dont_reorder; /* not part of a BA session */ - if (ack_policy == IEEE80211_QOS_CTL_ACK_POLICY_NOACK) + if (ack_policy != IEEE80211_QOS_CTL_ACK_POLICY_BLOCKACK && + ack_policy != IEEE80211_QOS_CTL_ACK_POLICY_NORMAL) goto dont_reorder; /* new, potentially un-ordered, ampdu frame - process it */ @@ -1944,8 +1945,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) int keyid = rx->sta->ptk_idx; sta_ptk = rcu_dereference(rx->sta->ptk[keyid]); - if (ieee80211_has_protected(fc) && - !(status->flag & RX_FLAG_IV_STRIPPED)) { + if (ieee80211_has_protected(fc)) { cs = rx->sta->cipher_scheme; keyid = ieee80211_get_keyid(rx->skb, cs); @@ -1975,11 +1975,10 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) if (mmie_keyidx < NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS || mmie_keyidx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS + - NUM_DEFAULT_BEACON_KEYS) { - if (rx->sdata->dev) - cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev, - skb->data, - skb->len); + NUM_DEFAULT_BEACON_KEYS) { + cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev, + skb->data, + skb->len); return RX_DROP_MONITOR; /* unexpected BIP keyidx */ } @@ -2127,42 +2126,26 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) /* either the frame has been decrypted or will be dropped */ status->flag |= RX_FLAG_DECRYPTED; - if (unlikely(ieee80211_is_beacon(fc) && result == RX_DROP_UNUSABLE && - rx->sdata->dev)) + if (unlikely(ieee80211_is_beacon(fc) && result == RX_DROP_UNUSABLE)) cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev, skb->data, skb->len); return result; } -void ieee80211_init_frag_cache(struct ieee80211_fragment_cache *cache) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(cache->entries); i++) - skb_queue_head_init(&cache->entries[i].skb_list); -} - -void ieee80211_destroy_frag_cache(struct ieee80211_fragment_cache *cache) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(cache->entries); i++) - __skb_queue_purge(&cache->entries[i].skb_list); -} - static inline struct ieee80211_fragment_entry * -ieee80211_reassemble_add(struct ieee80211_fragment_cache *cache, +ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata, unsigned int frag, unsigned int seq, int rx_queue, struct sk_buff **skb) { struct ieee80211_fragment_entry *entry; - entry = &cache->entries[cache->next++]; - if (cache->next >= IEEE80211_FRAGMENT_MAX) - cache->next = 0; + entry = &sdata->fragments[sdata->fragment_next++]; + if (sdata->fragment_next >= IEEE80211_FRAGMENT_MAX) + sdata->fragment_next = 0; - __skb_queue_purge(&entry->skb_list); + if (!skb_queue_empty(&entry->skb_list)) + __skb_queue_purge(&entry->skb_list); __skb_queue_tail(&entry->skb_list, *skb); /* no need for locking */ *skb = NULL; @@ -2177,14 +2160,14 @@ ieee80211_reassemble_add(struct ieee80211_fragment_cache *cache, } static inline struct ieee80211_fragment_entry * -ieee80211_reassemble_find(struct ieee80211_fragment_cache *cache, +ieee80211_reassemble_find(struct ieee80211_sub_if_data *sdata, unsigned int frag, unsigned int seq, int rx_queue, struct ieee80211_hdr *hdr) { struct ieee80211_fragment_entry *entry; int i, idx; - idx = cache->next; + idx = sdata->fragment_next; for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) { struct ieee80211_hdr *f_hdr; struct sk_buff *f_skb; @@ -2193,7 +2176,7 @@ ieee80211_reassemble_find(struct ieee80211_fragment_cache *cache, if (idx < 0) idx = IEEE80211_FRAGMENT_MAX - 1; - entry = &cache->entries[idx]; + entry = &sdata->fragments[idx]; if (skb_queue_empty(&entry->skb_list) || entry->seq != seq || entry->rx_queue != rx_queue || entry->last_frag + 1 != frag) @@ -2221,27 +2204,15 @@ ieee80211_reassemble_find(struct ieee80211_fragment_cache *cache, return NULL; } -static bool requires_sequential_pn(struct ieee80211_rx_data *rx, __le16 fc) -{ - return rx->key && - (rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP || - rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP_256 || - rx->key->conf.cipher == WLAN_CIPHER_SUITE_GCMP || - rx->key->conf.cipher == WLAN_CIPHER_SUITE_GCMP_256) && - ieee80211_has_protected(fc); -} - static ieee80211_rx_result debug_noinline ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) { - struct ieee80211_fragment_cache *cache = &rx->sdata->frags; struct ieee80211_hdr *hdr; u16 sc; __le16 fc; unsigned int frag, seq; struct ieee80211_fragment_entry *entry; struct sk_buff *skb; - struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); hdr = (struct ieee80211_hdr *)rx->skb->data; fc = hdr->frame_control; @@ -2252,15 +2223,14 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) sc = le16_to_cpu(hdr->seq_ctrl); frag = sc & IEEE80211_SCTL_FRAG; - if (rx->sta) - cache = &rx->sta->frags; + if (is_multicast_ether_addr(hdr->addr1)) { + I802_DEBUG_INC(rx->local->dot11MulticastReceivedFrameCount); + goto out_no_led; + } if (likely(!ieee80211_has_morefrags(fc) && frag == 0)) goto out; - if (is_multicast_ether_addr(hdr->addr1)) - return RX_DROP_MONITOR; - I802_DEBUG_INC(rx->local->rx_handlers_fragments); if (skb_linearize(rx->skb)) @@ -2276,17 +2246,20 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) if (frag == 0) { /* This is the first fragment of a new frame. */ - entry = ieee80211_reassemble_add(cache, frag, seq, + entry = ieee80211_reassemble_add(rx->sdata, frag, seq, rx->seqno_idx, &(rx->skb)); - if (requires_sequential_pn(rx, fc)) { + if (rx->key && + (rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP || + rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP_256 || + rx->key->conf.cipher == WLAN_CIPHER_SUITE_GCMP || + rx->key->conf.cipher == WLAN_CIPHER_SUITE_GCMP_256) && + ieee80211_has_protected(fc)) { int queue = rx->security_idx; /* Store CCMP/GCMP PN so that we can verify that the * next fragment has a sequential PN value. */ entry->check_sequential_pn = true; - entry->is_protected = true; - entry->key_color = rx->key->color; memcpy(entry->last_pn, rx->key->u.ccmp.rx_pn[queue], IEEE80211_CCMP_PN_LEN); @@ -2298,11 +2271,6 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) sizeof(rx->key->u.gcmp.rx_pn[queue])); BUILD_BUG_ON(IEEE80211_CCMP_PN_LEN != IEEE80211_GCMP_PN_LEN); - } else if (rx->key && - (ieee80211_has_protected(fc) || - (status->flag & RX_FLAG_DECRYPTED))) { - entry->is_protected = true; - entry->key_color = rx->key->color; } return RX_QUEUED; } @@ -2310,7 +2278,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) /* This is a fragment for a frame that should already be pending in * fragment cache. Add this fragment to the end of the pending entry. */ - entry = ieee80211_reassemble_find(cache, frag, seq, + entry = ieee80211_reassemble_find(rx->sdata, frag, seq, rx->seqno_idx, hdr); if (!entry) { I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag); @@ -2325,39 +2293,25 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) if (entry->check_sequential_pn) { int i; u8 pn[IEEE80211_CCMP_PN_LEN], *rpn; + int queue; - if (!requires_sequential_pn(rx, fc)) - return RX_DROP_UNUSABLE; - - /* Prevent mixed key and fragment cache attacks */ - if (entry->key_color != rx->key->color) + if (!rx->key || + (rx->key->conf.cipher != WLAN_CIPHER_SUITE_CCMP && + rx->key->conf.cipher != WLAN_CIPHER_SUITE_CCMP_256 && + rx->key->conf.cipher != WLAN_CIPHER_SUITE_GCMP && + rx->key->conf.cipher != WLAN_CIPHER_SUITE_GCMP_256)) return RX_DROP_UNUSABLE; - memcpy(pn, entry->last_pn, IEEE80211_CCMP_PN_LEN); for (i = IEEE80211_CCMP_PN_LEN - 1; i >= 0; i--) { pn[i]++; if (pn[i]) break; } - - rpn = rx->ccm_gcm.pn; + queue = rx->security_idx; + rpn = rx->key->u.ccmp.rx_pn[queue]; if (memcmp(pn, rpn, IEEE80211_CCMP_PN_LEN)) return RX_DROP_UNUSABLE; memcpy(entry->last_pn, pn, IEEE80211_CCMP_PN_LEN); - } else if (entry->is_protected && - (!rx->key || - (!ieee80211_has_protected(fc) && - !(status->flag & RX_FLAG_DECRYPTED)) || - rx->key->color != entry->key_color)) { - /* Drop this as a mixed key or fragment cache attack, even - * if for TKIP Michael MIC should protect us, and WEP is a - * lost cause anyway. - */ - return RX_DROP_UNUSABLE; - } else if (entry->is_protected && rx->key && - entry->key_color != rx->key->color && - (status->flag & RX_FLAG_DECRYPTED)) { - return RX_DROP_UNUSABLE; } skb_pull(rx->skb, ieee80211_hdrlen(fc)); @@ -2386,6 +2340,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) out: ieee80211_led_rx(rx->local); + out_no_led: if (rx->sta) rx->sta->rx_stats.packets++; return RX_CONTINUE; @@ -2549,13 +2504,13 @@ static bool ieee80211_frame_allowed(struct ieee80211_rx_data *rx, __le16 fc) struct ethhdr *ehdr = (struct ethhdr *) rx->skb->data; /* - * Allow EAPOL frames to us/the PAE group address regardless of - * whether the frame was encrypted or not, and always disallow - * all other destination addresses for them. + * Allow EAPOL frames to us/the PAE group address regardless + * of whether the frame was encrypted or not. */ - if (unlikely(ehdr->h_proto == rx->sdata->control_port_protocol)) - return ether_addr_equal(ehdr->h_dest, rx->sdata->vif.addr) || - ether_addr_equal(ehdr->h_dest, pae_group_addr); + if (ehdr->h_proto == rx->sdata->control_port_protocol && + (ether_addr_equal(ehdr->h_dest, rx->sdata->vif.addr) || + ether_addr_equal(ehdr->h_dest, pae_group_addr))) + return true; if (ieee80211_802_1x_port_control(rx) || ieee80211_drop_unencrypted(rx, fc)) @@ -2580,28 +2535,8 @@ static void ieee80211_deliver_skb_to_local_stack(struct sk_buff *skb, cfg80211_rx_control_port(dev, skb, noencrypt); dev_kfree_skb(skb); } else { - struct ethhdr *ehdr = (void *)skb_mac_header(skb); - memset(skb->cb, 0, sizeof(skb->cb)); - /* - * 802.1X over 802.11 requires that the authenticator address - * be used for EAPOL frames. However, 802.1X allows the use of - * the PAE group address instead. If the interface is part of - * a bridge and we pass the frame with the PAE group address, - * then the bridge will forward it to the network (even if the - * client was not associated yet), which isn't supposed to - * happen. - * To avoid that, rewrite the destination address to our own - * address, so that the authenticator (e.g. hostapd) will see - * the frame, but bridge won't forward it anywhere else. Note - * that due to earlier filtering, the only other address can - * be the PAE group address. - */ - if (unlikely(skb->protocol == sdata->control_port_protocol && - !ether_addr_equal(ehdr->h_dest, sdata->vif.addr))) - ether_addr_copy(ehdr->h_dest, sdata->vif.addr); - /* deliver to local stack */ if (rx->list) list_add_tail(&skb->list, rx->list); @@ -2641,7 +2576,6 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx) if ((sdata->vif.type == NL80211_IFTYPE_AP || sdata->vif.type == NL80211_IFTYPE_AP_VLAN) && !(sdata->flags & IEEE80211_SDATA_DONT_BRIDGE_PACKETS) && - ehdr->h_proto != rx->sdata->control_port_protocol && (sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->u.vlan.sta)) { if (is_multicast_ether_addr(ehdr->h_dest) && ieee80211_vif_get_num_mcast_if(sdata) != 0) { @@ -2751,7 +2685,7 @@ __ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx, u8 data_offset) if (ieee80211_data_to_8023_exthdr(skb, ðhdr, rx->sdata->vif.addr, rx->sdata->vif.type, - data_offset, true)) + data_offset)) return RX_DROP_UNUSABLE; ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr, @@ -2808,23 +2742,6 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx) if (is_multicast_ether_addr(hdr->addr1)) return RX_DROP_UNUSABLE; - if (rx->key) { - /* - * We should not receive A-MSDUs on pre-HT connections, - * and HT connections cannot use old ciphers. Thus drop - * them, as in those cases we couldn't even have SPP - * A-MSDUs or such. - */ - switch (rx->key->conf.cipher) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - case WLAN_CIPHER_SUITE_TKIP: - return RX_DROP_UNUSABLE; - default: - break; - } - } - return __ieee80211_rx_h_amsdu(rx, 0); } @@ -2911,13 +2828,13 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) ether_addr_equal(sdata->vif.addr, hdr->addr3)) return RX_CONTINUE; - ac = ieee802_1d_to_ac[skb->priority]; + ac = ieee80211_select_queue_80211(sdata, skb, hdr); q = sdata->vif.hw_queue[ac]; if (ieee80211_queue_stopped(&local->hw, q)) { IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_congestion); return RX_DROP_MONITOR; } - skb_set_queue_mapping(skb, ac); + skb_set_queue_mapping(skb, q); if (!--mesh_hdr->ttl) { if (!is_multicast_ether_addr(hdr->addr1)) @@ -4066,8 +3983,7 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx) if (!bssid) return false; if (ether_addr_equal(sdata->vif.addr, hdr->addr2) || - ether_addr_equal(sdata->u.ibss.bssid, hdr->addr2) || - !is_valid_ether_addr(hdr->addr2)) + ether_addr_equal(sdata->u.ibss.bssid, hdr->addr2)) return false; if (ieee80211_is_beacon(hdr->frame_control)) return true; @@ -4275,8 +4191,6 @@ void ieee80211_check_fast_rx(struct sta_info *sta) rcu_read_lock(); key = rcu_dereference(sta->ptk[sta->ptk_idx]); - if (!key) - key = rcu_dereference(sdata->default_unicast_key); if (key) { switch (key->conf.cipher) { case WLAN_CIPHER_SUITE_TKIP: @@ -4801,7 +4715,7 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta, goto drop; break; case RX_ENC_VHT: - if (WARN_ONCE(status->rate_idx > 11 || + if (WARN_ONCE(status->rate_idx > 9 || !status->nss || status->nss > 8, "Rate marked as a VHT rate but data is invalid: MCS: %d, NSS: %d\n", diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index b241ff8c015a9..d4cc9ac2d7033 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -227,8 +227,6 @@ ieee80211_bss_info_update(struct ieee80211_local *local, rx_status, beacon); } - kfree(elems.nontx_profile); - return bss; } @@ -253,24 +251,13 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb) struct ieee80211_mgmt *mgmt = (void *)skb->data; struct ieee80211_bss *bss; struct ieee80211_channel *channel; - size_t min_hdr_len = offsetof(struct ieee80211_mgmt, - u.probe_resp.variable); - - if (!ieee80211_is_probe_resp(mgmt->frame_control) && - !ieee80211_is_beacon(mgmt->frame_control) && - !ieee80211_is_s1g_beacon(mgmt->frame_control)) - return; if (ieee80211_is_s1g_beacon(mgmt->frame_control)) { - if (ieee80211_is_s1g_short_beacon(mgmt->frame_control)) - min_hdr_len = offsetof(struct ieee80211_ext, - u.s1g_short_beacon.variable); - else - min_hdr_len = offsetof(struct ieee80211_ext, - u.s1g_beacon); - } - - if (skb->len < min_hdr_len) + if (skb->len < 15) + return; + } else if (skb->len < 24 || + (!ieee80211_is_probe_resp(mgmt->frame_control) && + !ieee80211_is_beacon(mgmt->frame_control))) return; sdata1 = rcu_dereference(local->scan_sdata); @@ -279,16 +266,6 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb) if (likely(!sdata1 && !sdata2)) return; - if (test_and_clear_bit(SCAN_BEACON_WAIT, &local->scanning)) { - /* - * we were passive scanning because of radar/no-IR, but - * the beacon/proberesp rx gives us an opportunity to upgrade - * to active scan - */ - set_bit(SCAN_BEACON_DONE, &local->scanning); - ieee80211_queue_delayed_work(&local->hw, &local->scan_work, 0); - } - if (ieee80211_is_probe_resp(mgmt->frame_control)) { struct cfg80211_scan_request *scan_req; struct cfg80211_sched_scan_request *sched_scan_req; @@ -463,19 +440,16 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) scan_req = rcu_dereference_protected(local->scan_req, lockdep_is_held(&local->mtx)); + if (scan_req != local->int_scan_req) { + local->scan_info.aborted = aborted; + cfg80211_scan_done(scan_req, &local->scan_info); + } RCU_INIT_POINTER(local->scan_req, NULL); RCU_INIT_POINTER(local->scan_sdata, NULL); local->scanning = 0; local->scan_chandef.chan = NULL; - synchronize_rcu(); - - if (scan_req != local->int_scan_req) { - local->scan_info.aborted = aborted; - cfg80211_scan_done(scan_req, &local->scan_info); - } - /* Set power back to normal operating levels. */ ieee80211_hw_config(local, 0); @@ -798,8 +772,6 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, IEEE80211_CHAN_RADAR)) || !req->n_ssids) { next_delay = IEEE80211_PASSIVE_CHANNEL_TIME; - if (req->n_ssids) - set_bit(SCAN_BEACON_WAIT, &local->scanning); } else { ieee80211_scan_state_send_probe(local, &next_delay); next_delay = IEEE80211_CHANNEL_TIME; @@ -1011,8 +983,6 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, !scan_req->n_ssids) { *next_delay = IEEE80211_PASSIVE_CHANNEL_TIME; local->next_scan_state = SCAN_DECISION; - if (scan_req->n_ssids) - set_bit(SCAN_BEACON_WAIT, &local->scanning); return; } @@ -1105,8 +1075,6 @@ void ieee80211_scan_work(struct work_struct *work) goto out; } - clear_bit(SCAN_BEACON_WAIT, &local->scanning); - /* * as long as no delay is required advance immediately * without scheduling a new work @@ -1117,10 +1085,6 @@ void ieee80211_scan_work(struct work_struct *work) goto out_complete; } - if (test_and_clear_bit(SCAN_BEACON_DONE, &local->scanning) && - local->next_scan_state == SCAN_DECISION) - local->next_scan_state = SCAN_SEND_PROBE; - switch (local->next_scan_state) { case SCAN_DECISION: /* if no more bands/channels left, complete scan */ diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c index 76747bfdaddd0..ae1cb2c687224 100644 --- a/net/mac80211/spectmgmt.c +++ b/net/mac80211/spectmgmt.c @@ -133,20 +133,16 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, } if (wide_bw_chansw_ie) { - u8 new_seg1 = wide_bw_chansw_ie->new_center_freq_seg1; struct ieee80211_vht_operation vht_oper = { .chan_width = wide_bw_chansw_ie->new_channel_width, .center_freq_seg0_idx = wide_bw_chansw_ie->new_center_freq_seg0, - .center_freq_seg1_idx = new_seg1, + .center_freq_seg1_idx = + wide_bw_chansw_ie->new_center_freq_seg1, /* .basic_mcs_set doesn't matter */ }; - struct ieee80211_ht_operation ht_oper = { - .operation_mode = - cpu_to_le16(new_seg1 << - IEEE80211_HT_OP_MODE_CCFS2_SHIFT), - }; + struct ieee80211_ht_operation ht_oper = {}; /* default, for the case of IEEE80211_VHT_CHANWIDTH_USE_HT, * to the previously parsed chandef diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index cee39ae52245c..ec6973ee88ef4 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -4,7 +4,7 @@ * Copyright 2006-2007 Jiri Benc * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015 - 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2021 Intel Corporation + * Copyright (C) 2018-2020 Intel Corporation */ #include @@ -392,8 +392,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, u64_stats_init(&sta->rx_stats.syncp); - ieee80211_init_frag_cache(&sta->frags); - sta->sta_state = IEEE80211_STA_NONE; /* Mark TID as unreserved */ @@ -645,13 +643,13 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU) /* check if STA exists already */ if (sta_info_get_bss(sdata, sta->sta.addr)) { err = -EEXIST; - goto out_cleanup; + goto out_err; } sinfo = kzalloc(sizeof(struct station_info), GFP_KERNEL); if (!sinfo) { err = -ENOMEM; - goto out_cleanup; + goto out_err; } local->num_sta++; @@ -707,8 +705,8 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU) out_drop_sta: local->num_sta--; synchronize_net(); - out_cleanup: cleanup_single_sta(sta); + out_err: mutex_unlock(&local->sta_mtx); kfree(sinfo); rcu_read_lock(); @@ -1104,8 +1102,6 @@ static void __sta_info_destroy_part2(struct sta_info *sta) ieee80211_sta_debugfs_remove(sta); - ieee80211_destroy_frag_cache(&sta->frags); - cleanup_single_sta(sta); } @@ -1398,6 +1394,11 @@ static void ieee80211_send_null_response(struct sta_info *sta, int tid, struct ieee80211_tx_info *info; struct ieee80211_chanctx_conf *chanctx_conf; + /* Don't send NDPs when STA is connected HE */ + if (sdata->vif.type == NL80211_IFTYPE_STATION && + !(sdata->u.mgd.flags & IEEE80211_STA_DISABLE_HE)) + return; + if (qos) { fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_NULLFUNC | @@ -2088,9 +2089,10 @@ static struct ieee80211_sta_rx_stats * sta_get_last_rx_stats(struct sta_info *sta) { struct ieee80211_sta_rx_stats *stats = &sta->rx_stats; + struct ieee80211_local *local = sta->local; int cpu; - if (!sta->pcpu_rx_stats) + if (!ieee80211_hw_check(&local->hw, USES_RSS)) return stats; for_each_possible_cpu(cpu) { @@ -2175,9 +2177,9 @@ static inline u64 sta_get_tidstats_msdu(struct ieee80211_sta_rx_stats *rxstats, u64 value; do { - start = u64_stats_fetch_begin_irq(&rxstats->syncp); + start = u64_stats_fetch_begin(&rxstats->syncp); value = rxstats->msdu[tid]; - } while (u64_stats_fetch_retry_irq(&rxstats->syncp, start)); + } while (u64_stats_fetch_retry(&rxstats->syncp, start)); return value; } @@ -2190,7 +2192,9 @@ static void sta_set_tidstats(struct sta_info *sta, int cpu; if (!(tidstats->filled & BIT(NL80211_TID_STATS_RX_MSDU))) { - tidstats->rx_msdu += sta_get_tidstats_msdu(&sta->rx_stats, tid); + if (!ieee80211_hw_check(&local->hw, USES_RSS)) + tidstats->rx_msdu += + sta_get_tidstats_msdu(&sta->rx_stats, tid); if (sta->pcpu_rx_stats) { for_each_possible_cpu(cpu) { @@ -2241,9 +2245,9 @@ static inline u64 sta_get_stats_bytes(struct ieee80211_sta_rx_stats *rxstats) u64 value; do { - start = u64_stats_fetch_begin_irq(&rxstats->syncp); + start = u64_stats_fetch_begin(&rxstats->syncp); value = rxstats->bytes; - } while (u64_stats_fetch_retry_irq(&rxstats->syncp, start)); + } while (u64_stats_fetch_retry(&rxstats->syncp, start)); return value; } @@ -2269,6 +2273,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo, sinfo->rx_beacon = sdata->u.mgd.count_beacon_signal; drv_sta_statistics(local, sdata, &sta->sta, sinfo); + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_INACTIVE_TIME) | BIT_ULL(NL80211_STA_INFO_STA_FLAGS) | BIT_ULL(NL80211_STA_INFO_BSS_PARAM) | @@ -2303,7 +2308,8 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo, if (!(sinfo->filled & (BIT_ULL(NL80211_STA_INFO_RX_BYTES64) | BIT_ULL(NL80211_STA_INFO_RX_BYTES)))) { - sinfo->rx_bytes += sta_get_stats_bytes(&sta->rx_stats); + if (!ieee80211_hw_check(&local->hw, USES_RSS)) + sinfo->rx_bytes += sta_get_stats_bytes(&sta->rx_stats); if (sta->pcpu_rx_stats) { for_each_possible_cpu(cpu) { diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index b9e5f8e8f29cc..7afd07636b81d 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -3,7 +3,7 @@ * Copyright 2002-2005, Devicescape Software, Inc. * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright(c) 2015-2017 Intel Deutschland GmbH - * Copyright(c) 2020-2021 Intel Corporation + * Copyright(c) 2020 Intel Corporation */ #ifndef STA_INFO_H @@ -190,7 +190,6 @@ struct tid_ampdu_tx { u8 stop_initiator; bool tx_stop; u16 buf_size; - u16 ssn; u16 failed_bar_ssn; bool bar_pending; @@ -437,34 +436,6 @@ struct ieee80211_sta_rx_stats { u64 msdu[IEEE80211_NUM_TIDS + 1]; }; -/* - * IEEE 802.11-2016 (10.6 "Defragmentation") recommends support for "concurrent - * reception of at least one MSDU per access category per associated STA" - * on APs, or "at least one MSDU per access category" on other interface types. - * - * This limit can be increased by changing this define, at the cost of slower - * frame reassembly and increased memory use while fragments are pending. - */ -#define IEEE80211_FRAGMENT_MAX 4 - -struct ieee80211_fragment_entry { - struct sk_buff_head skb_list; - unsigned long first_frag_time; - u16 seq; - u16 extra_len; - u16 last_frag; - u8 rx_queue; - u8 check_sequential_pn:1, /* needed for CCMP/GCMP */ - is_protected:1; - u8 last_pn[6]; /* PN of the last fragment if CCMP was used */ - unsigned int key_color; -}; - -struct ieee80211_fragment_cache { - struct ieee80211_fragment_entry entries[IEEE80211_FRAGMENT_MAX]; - unsigned int next; -}; - /* * The bandwidth threshold below which the per-station CoDel parameters will be * scaled to be more lenient (to prevent starvation of slow stations). This @@ -558,7 +529,6 @@ struct ieee80211_fragment_cache { * @status_stats.last_ack_signal: last ACK signal * @status_stats.ack_signal_filled: last ACK signal validity * @status_stats.avg_ack_signal: average ACK signal - * @frags: fragment cache */ struct sta_info { /* General information, mostly static */ @@ -667,8 +637,6 @@ struct sta_info { struct cfg80211_chan_def tdls_chandef; - struct ieee80211_fragment_cache frags; - /* keep last! */ struct ieee80211_sta sta; }; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index bbbcc678c655c..56a4d0d20a267 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -662,7 +662,7 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) if (!skip_hw && tx->key && tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) info->control.hw_key = &tx->key->conf; - } else if (ieee80211_is_data_present(hdr->frame_control) && tx->sta && + } else if (!ieee80211_is_mgmt(hdr->frame_control) && tx->sta && test_sta_flag(tx->sta, WLAN_STA_USES_ENCRYPTION)) { return TX_DROP; } @@ -2030,26 +2030,6 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, ieee80211_tx(sdata, sta, skb, false); } -static bool ieee80211_validate_radiotap_len(struct sk_buff *skb) -{ - struct ieee80211_radiotap_header *rthdr = - (struct ieee80211_radiotap_header *)skb->data; - - /* check for not even having the fixed radiotap header part */ - if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header))) - return false; /* too short to be possibly valid */ - - /* is it a header version we can trust to find length from? */ - if (unlikely(rthdr->it_version)) - return false; /* only version 0 is supported */ - - /* does the skb contain enough to deliver on the alleged length? */ - if (unlikely(skb->len < ieee80211_get_radiotap_len(skb->data))) - return false; /* skb too short for claimed rt header extent */ - - return true; -} - bool ieee80211_parse_tx_radiotap(struct sk_buff *skb, struct net_device *dev) { @@ -2058,6 +2038,8 @@ bool ieee80211_parse_tx_radiotap(struct sk_buff *skb, struct ieee80211_radiotap_header *rthdr = (struct ieee80211_radiotap_header *) skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_supported_band *sband = + local->hw.wiphy->bands[info->band]; int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len, NULL); u16 txflags; @@ -2070,8 +2052,17 @@ bool ieee80211_parse_tx_radiotap(struct sk_buff *skb, u8 vht_mcs = 0, vht_nss = 0; int i; - if (!ieee80211_validate_radiotap_len(skb)) - return false; + /* check for not even having the fixed radiotap header part */ + if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header))) + return false; /* too short to be possibly valid */ + + /* is it a header version we can trust to find length from? */ + if (unlikely(rthdr->it_version)) + return false; /* only version 0 is supported */ + + /* does the skb contain enough to deliver on the alleged length? */ + if (unlikely(skb->len < ieee80211_get_radiotap_len(skb->data))) + return false; /* skb too short for claimed rt header extent */ info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT | IEEE80211_TX_CTL_DONTFRAG; @@ -2177,11 +2168,7 @@ bool ieee80211_parse_tx_radiotap(struct sk_buff *skb, } vht_mcs = iterator.this_arg[4] >> 4; - if (vht_mcs > 11) - vht_mcs = 0; vht_nss = iterator.this_arg[4] & 0xF; - if (!vht_nss || vht_nss > 8) - vht_nss = 1; break; /* @@ -2199,9 +2186,6 @@ bool ieee80211_parse_tx_radiotap(struct sk_buff *skb, return false; if (rate_found) { - struct ieee80211_supported_band *sband = - local->hw.wiphy->bands[info->band]; - info->control.flags |= IEEE80211_TX_CTRL_RATE_INJECT; for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { @@ -2215,7 +2199,7 @@ bool ieee80211_parse_tx_radiotap(struct sk_buff *skb, } else if (rate_flags & IEEE80211_TX_RC_VHT_MCS) { ieee80211_rate_set_vht(info->control.rates, vht_mcs, vht_nss); - } else if (sband) { + } else { for (i = 0; i < sband->n_bitrates; i++) { if (rate * 5 != sband->bitrates[i].bitrate) continue; @@ -2252,8 +2236,8 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, info->flags = IEEE80211_TX_CTL_REQ_TX_STATUS | IEEE80211_TX_CTL_INJECTED; - /* Sanity-check the length of the radiotap header */ - if (!ieee80211_validate_radiotap_len(skb)) + /* Sanity-check and process the injection radiotap header */ + if (!ieee80211_parse_tx_radiotap(skb, dev)) goto fail; /* we now know there is a radiotap header with a length we can use */ @@ -2369,14 +2353,6 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, info->band = chandef->chan->band; - /* - * Process the radiotap header. This will now take into account the - * selected chandef above to accurately set injection rates and - * retransmissions. - */ - if (!ieee80211_parse_tx_radiotap(skb, dev)) - goto fail_rcu; - /* remove the injection radiotap header */ skb_pull(skb, len_rthdr); @@ -3233,9 +3209,7 @@ static bool ieee80211_amsdu_prepare_head(struct ieee80211_sub_if_data *sdata, if (info->control.flags & IEEE80211_TX_CTRL_AMSDU) return true; - if (!ieee80211_amsdu_realloc_pad(local, skb, - sizeof(*amsdu_hdr) + - local->hw.extra_tx_headroom)) + if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(*amsdu_hdr))) return false; data = skb_push(skb, sizeof(*amsdu_hdr)); @@ -3369,14 +3343,6 @@ static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata, if (!ieee80211_amsdu_prepare_head(sdata, fast_tx, head)) goto out; - /* If n == 2, the "while (*frag_tail)" loop above didn't execute - * and frag_tail should be &skb_shinfo(head)->frag_list. - * However, ieee80211_amsdu_prepare_head() can reallocate it. - * Reload frag_tail to have it pointing to the correct place. - */ - if (n == 2) - frag_tail = &skb_shinfo(head)->frag_list; - /* * Pad out the previous subframe to a multiple of 4 by adding the * padding to the next one, that's being added. Note that head->len @@ -3639,7 +3605,7 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw, test_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txqi->flags)) goto out; - if (vif->txqs_stopped[txq->ac]) { + if (vif->txqs_stopped[ieee80211_ac_from_tid(txq->tid)]) { set_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txqi->flags); goto out; } @@ -3870,7 +3836,7 @@ void __ieee80211_schedule_txq(struct ieee80211_hw *hw, * get immediately moved to the back of the list on the next * call to ieee80211_next_txq(). */ - if (txqi->txq.sta && local->airtime_flags && + if (txqi->txq.sta && wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AIRTIME_FAIRNESS)) list_add(&txqi->schedule_order, @@ -4312,6 +4278,7 @@ netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb, struct ethhdr *ehdr = (struct ethhdr *)skb->data; struct ieee80211_key *key; struct sta_info *sta; + bool offload = true; if (unlikely(skb->len < ETH_HLEN)) { kfree_skb(skb); @@ -4327,22 +4294,18 @@ netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb, if (unlikely(IS_ERR_OR_NULL(sta) || !sta->uploaded || !test_sta_flag(sta, WLAN_STA_AUTHORIZED) || - sdata->control_port_protocol == ehdr->h_proto)) - goto skip_offload; - - key = rcu_dereference(sta->ptk[sta->ptk_idx]); - if (!key) - key = rcu_dereference(sdata->default_unicast_key); - - if (key && (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) || - key->conf.cipher == WLAN_CIPHER_SUITE_TKIP)) - goto skip_offload; - - ieee80211_8023_xmit(sdata, dev, sta, key, skb); - goto out; + sdata->control_port_protocol == ehdr->h_proto)) + offload = false; + else if ((key = rcu_dereference(sta->ptk[sta->ptk_idx])) && + (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) || + key->conf.cipher == WLAN_CIPHER_SUITE_TKIP)) + offload = false; + + if (offload) + ieee80211_8023_xmit(sdata, dev, sta, key, skb); + else + ieee80211_subif_start_xmit(skb, dev); -skip_offload: - ieee80211_subif_start_xmit(skb, dev); out: rcu_read_unlock(); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 7fa6efa8b83c1..94e624e9439b7 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -950,16 +950,11 @@ static void ieee80211_parse_extension_element(u32 *crc, struct ieee802_11_elems *elems) { const void *data = elem->data + 1; - u8 len; - - if (!elem->datalen) - return; - - len = elem->datalen - 1; + u8 len = elem->datalen - 1; switch (elem->data[0]) { case WLAN_EID_EXT_HE_MU_EDCA: - if (len >= sizeof(*elems->mu_edca_param_set)) { + if (len == sizeof(*elems->mu_edca_param_set)) { elems->mu_edca_param_set = data; if (crc) *crc = crc32_be(*crc, (void *)elem, @@ -972,7 +967,7 @@ static void ieee80211_parse_extension_element(u32 *crc, break; case WLAN_EID_EXT_HE_OPERATION: if (len >= sizeof(*elems->he_operation) && - len >= ieee80211_he_oper_size(data) - 1) { + len == ieee80211_he_oper_size(data) - 1) { if (crc) *crc = crc32_be(*crc, (void *)elem, elem->datalen + 2); @@ -980,7 +975,7 @@ static void ieee80211_parse_extension_element(u32 *crc, } break; case WLAN_EID_EXT_UORA: - if (len >= 1) + if (len == 1) elems->uora_element = data; break; case WLAN_EID_EXT_MAX_CHANNEL_SWITCH_TIME: @@ -988,7 +983,7 @@ static void ieee80211_parse_extension_element(u32 *crc, elems->max_channel_switch_time = data; break; case WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION: - if (len >= sizeof(*elems->mbssid_config_ie)) + if (len == sizeof(*elems->mbssid_config_ie)) elems->mbssid_config_ie = data; break; case WLAN_EID_EXT_HE_SPR: @@ -997,7 +992,7 @@ static void ieee80211_parse_extension_element(u32 *crc, elems->he_spr = data; break; case WLAN_EID_EXT_HE_6GHZ_CAPA: - if (len >= sizeof(*elems->he_6ghz_capa)) + if (len == sizeof(*elems->he_6ghz_capa)) elems->he_6ghz_capa = data; break; } @@ -1086,14 +1081,14 @@ _ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, switch (id) { case WLAN_EID_LINK_ID: - if (elen + 2 < sizeof(struct ieee80211_tdls_lnkie)) { + if (elen + 2 != sizeof(struct ieee80211_tdls_lnkie)) { elem_parse_failed = true; break; } elems->lnk_id = (void *)(pos - 2); break; case WLAN_EID_CHAN_SWITCH_TIMING: - if (elen < sizeof(struct ieee80211_ch_switch_timing)) { + if (elen != sizeof(struct ieee80211_ch_switch_timing)) { elem_parse_failed = true; break; } @@ -1124,6 +1119,10 @@ _ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, } else elem_parse_failed = true; break; + case WLAN_EID_CHALLENGE: + elems->challenge = pos; + elems->challenge_len = elen; + break; case WLAN_EID_VENDOR_SPECIFIC: if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 && pos[2] == 0xf2) { @@ -1252,7 +1251,7 @@ _ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, elems->sec_chan_offs = (void *)pos; break; case WLAN_EID_CHAN_SWITCH_PARAM: - if (elen < + if (elen != sizeof(*elems->mesh_chansw_params_ie)) { elem_parse_failed = true; break; @@ -1261,7 +1260,7 @@ _ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, break; case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: if (!action || - elen < sizeof(*elems->wide_bw_chansw_ie)) { + elen != sizeof(*elems->wide_bw_chansw_ie)) { elem_parse_failed = true; break; } @@ -1280,7 +1279,7 @@ _ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, ie = cfg80211_find_ie(WLAN_EID_WIDE_BW_CHANNEL_SWITCH, pos, elen); if (ie) { - if (ie[1] >= sizeof(*elems->wide_bw_chansw_ie)) + if (ie[1] == sizeof(*elems->wide_bw_chansw_ie)) elems->wide_bw_chansw_ie = (void *)(ie + 2); else @@ -1324,7 +1323,7 @@ _ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, elems->cisco_dtpc_elem = pos; break; case WLAN_EID_ADDBA_EXT: - if (elen < sizeof(struct ieee80211_addba_ext_ie)) { + if (elen != sizeof(struct ieee80211_addba_ext_ie)) { elem_parse_failed = true; break; } @@ -1350,7 +1349,7 @@ _ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, elem, elems); break; case WLAN_EID_S1G_CAPABILITIES: - if (elen >= sizeof(*elems->s1g_capab)) + if (elen == sizeof(*elems->s1g_capab)) elems->s1g_capab = (void *)pos; else elem_parse_failed = true; @@ -1405,8 +1404,6 @@ static size_t ieee802_11_find_bssid_profile(const u8 *start, size_t len, for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, start, len) { if (elem->datalen < 2) continue; - if (elem->data[0] < 1 || elem->data[0] > 8) - continue; for_each_element(sub, elem->data + 1, elem->datalen - 1) { u8 new_bssid[ETH_ALEN]; @@ -1483,11 +1480,6 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE, nontransmitted_profile, nontransmitted_profile_len); - if (!nontransmitted_profile_len) { - nontransmitted_profile_len = 0; - kfree(nontransmitted_profile); - nontransmitted_profile = NULL; - } } crc = _ieee802_11_parse_elems_crc(start, len, action, elems, filter, @@ -1517,7 +1509,7 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, offsetofend(struct ieee80211_bssid_index, dtim_count)) elems->dtim_count = elems->bssid_index->dtim_count; - elems->nontx_profile = nontransmitted_profile; + kfree(nontransmitted_profile); return crc; } diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index b9404b0560871..2fb99325135a0 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -145,8 +145,8 @@ u16 __ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, bool qos; /* all mesh/ocb stations are required to support WME */ - if (sta && (sdata->vif.type == NL80211_IFTYPE_MESH_POINT || - sdata->vif.type == NL80211_IFTYPE_OCB)) + if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT || + sdata->vif.type == NL80211_IFTYPE_OCB) qos = true; else if (sta) qos = sta->sta.wme; diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index 4eed23e276104..91bf32af55e9a 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -3,7 +3,6 @@ * Copyright 2002-2004, Instant802 Networks, Inc. * Copyright 2008, Jouni Malinen * Copyright (C) 2016-2017 Intel Deutschland GmbH - * Copyright (C) 2020-2021 Intel Corporation */ #include @@ -168,8 +167,8 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx) update_iv: /* update IV in key information to be able to detect replays */ - rx->key->u.tkip.rx[rx->security_idx].iv32 = rx->tkip.iv32; - rx->key->u.tkip.rx[rx->security_idx].iv16 = rx->tkip.iv16; + rx->key->u.tkip.rx[rx->security_idx].iv32 = rx->tkip_iv32; + rx->key->u.tkip.rx[rx->security_idx].iv16 = rx->tkip_iv16; return RX_CONTINUE; @@ -295,8 +294,8 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx) key, skb->data + hdrlen, skb->len - hdrlen, rx->sta->sta.addr, hdr->addr1, hwaccel, rx->security_idx, - &rx->tkip.iv32, - &rx->tkip.iv16); + &rx->tkip_iv32, + &rx->tkip_iv16); if (res != TKIP_DECRYPT_OK) return RX_DROP_UNUSABLE; @@ -520,9 +519,6 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx, return RX_DROP_UNUSABLE; } - /* reload hdr - skb might have been reallocated */ - hdr = (void *)rx->skb->data; - data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN - mic_len; if (!rx->sta || data_len < 0) return RX_DROP_UNUSABLE; @@ -557,8 +553,6 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx, } memcpy(key->u.ccmp.rx_pn[queue], pn, IEEE80211_CCMP_PN_LEN); - if (unlikely(ieee80211_is_frag(hdr))) - memcpy(rx->ccm_gcm.pn, pn, IEEE80211_CCMP_PN_LEN); } /* Remove CCMP header and MIC */ @@ -752,9 +746,6 @@ ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data *rx) return RX_DROP_UNUSABLE; } - /* reload hdr - skb might have been reallocated */ - hdr = (void *)rx->skb->data; - data_len = skb->len - hdrlen - IEEE80211_GCMP_HDR_LEN - mic_len; if (!rx->sta || data_len < 0) return RX_DROP_UNUSABLE; @@ -790,8 +781,6 @@ ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data *rx) } memcpy(key->u.gcmp.rx_pn[queue], pn, IEEE80211_GCMP_PN_LEN); - if (unlikely(ieee80211_is_frag(hdr))) - memcpy(rx->ccm_gcm.pn, pn, IEEE80211_CCMP_PN_LEN); } /* Remove GCMP header and MIC */ diff --git a/net/wireless/Makefile b/net/wireless/Makefile index af590ae606b69..2eee93985ab0d 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -28,7 +28,7 @@ $(obj)/shipped-certs.c: $(wildcard $(srctree)/$(src)/certs/*.hex) @$(kecho) " GEN $@" @(echo '#include "reg.h"'; \ echo 'const u8 shipped_regdb_certs[] = {'; \ - echo | cat - $^ ; \ + cat $^ ; \ echo '};'; \ echo 'unsigned int shipped_regdb_certs_len = sizeof(shipped_regdb_certs);'; \ ) > $@ diff --git a/net/wireless/core.c b/net/wireless/core.c index 3b25b78896a28..240282c083aa7 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -5,7 +5,7 @@ * Copyright 2006-2010 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2021 Intel Corporation + * Copyright (C) 2018-2020 Intel Corporation */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -501,7 +501,6 @@ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv, INIT_WORK(&rdev->propagate_cac_done_wk, cfg80211_propagate_cac_done_wk); INIT_WORK(&rdev->mgmt_registrations_update_wk, cfg80211_mgmt_registrations_update_wk); - spin_lock_init(&rdev->mgmt_registrations_lock); #ifdef CONFIG_CFG80211_DEFAULT_PS rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; @@ -918,6 +917,9 @@ int wiphy_register(struct wiphy *wiphy) return res; } + /* set up regulatory info */ + wiphy_regulatory_register(wiphy); + list_add_rcu(&rdev->list, &cfg80211_rdev_list); cfg80211_rdev_list_generation++; @@ -928,9 +930,6 @@ int wiphy_register(struct wiphy *wiphy) cfg80211_debugfs_rdev_add(rdev); nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY); - /* set up regulatory info */ - wiphy_regulatory_register(wiphy); - if (wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) { struct regulatory_request request; @@ -1257,6 +1256,7 @@ void cfg80211_init_wdev(struct wireless_dev *wdev) INIT_LIST_HEAD(&wdev->event_list); spin_lock_init(&wdev->event_lock); INIT_LIST_HEAD(&wdev->mgmt_registrations); + spin_lock_init(&wdev->mgmt_registrations_lock); INIT_LIST_HEAD(&wdev->pmsr_list); spin_lock_init(&wdev->pmsr_lock); INIT_WORK(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk); diff --git a/net/wireless/core.h b/net/wireless/core.h index a3362a32acb32..7df91f9402124 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -101,8 +101,6 @@ struct cfg80211_registered_device { struct work_struct propagate_cac_done_wk; struct work_struct mgmt_registrations_update_wk; - /* lock for all wdev lists */ - spinlock_t mgmt_registrations_lock; /* must be last because of the way we do wiphy_priv(), * and it should at least be aligned to NETDEV_ALIGN */ diff --git a/net/wireless/debugfs.c b/net/wireless/debugfs.c index d80b06d669593..76b845f68ac89 100644 --- a/net/wireless/debugfs.c +++ b/net/wireless/debugfs.c @@ -65,10 +65,9 @@ static ssize_t ht40allow_map_read(struct file *file, { struct wiphy *wiphy = file->private_data; char *buf; - unsigned int offset = 0, buf_size = PAGE_SIZE, i; + unsigned int offset = 0, buf_size = PAGE_SIZE, i, r; enum nl80211_band band; struct ieee80211_supported_band *sband; - ssize_t r; buf = kzalloc(buf_size, GFP_KERNEL); if (!buf) diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 6dcfc5a348742..0ac820780437d 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -448,9 +448,9 @@ static void cfg80211_mgmt_registrations_update(struct wireless_dev *wdev) ASSERT_RTNL(); - spin_lock_bh(&rdev->mgmt_registrations_lock); + spin_lock_bh(&wdev->mgmt_registrations_lock); if (!wdev->mgmt_registrations_need_update) { - spin_unlock_bh(&rdev->mgmt_registrations_lock); + spin_unlock_bh(&wdev->mgmt_registrations_lock); return; } @@ -475,7 +475,7 @@ static void cfg80211_mgmt_registrations_update(struct wireless_dev *wdev) rcu_read_unlock(); wdev->mgmt_registrations_need_update = 0; - spin_unlock_bh(&rdev->mgmt_registrations_lock); + spin_unlock_bh(&wdev->mgmt_registrations_lock); rdev_update_mgmt_frame_registrations(rdev, wdev, &upd); } @@ -499,7 +499,6 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid, int match_len, bool multicast_rx, struct netlink_ext_ack *extack) { - struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); struct cfg80211_mgmt_registration *reg, *nreg; int err = 0; u16 mgmt_type; @@ -545,7 +544,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid, if (!nreg) return -ENOMEM; - spin_lock_bh(&rdev->mgmt_registrations_lock); + spin_lock_bh(&wdev->mgmt_registrations_lock); list_for_each_entry(reg, &wdev->mgmt_registrations, list) { int mlen = min(match_len, reg->match_len); @@ -580,7 +579,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid, list_add(&nreg->list, &wdev->mgmt_registrations); } wdev->mgmt_registrations_need_update = 1; - spin_unlock_bh(&rdev->mgmt_registrations_lock); + spin_unlock_bh(&wdev->mgmt_registrations_lock); cfg80211_mgmt_registrations_update(wdev); @@ -588,7 +587,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid, out: kfree(nreg); - spin_unlock_bh(&rdev->mgmt_registrations_lock); + spin_unlock_bh(&wdev->mgmt_registrations_lock); return err; } @@ -599,7 +598,7 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid) struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); struct cfg80211_mgmt_registration *reg, *tmp; - spin_lock_bh(&rdev->mgmt_registrations_lock); + spin_lock_bh(&wdev->mgmt_registrations_lock); list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) { if (reg->nlportid != nlportid) @@ -612,7 +611,7 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid) schedule_work(&rdev->mgmt_registrations_update_wk); } - spin_unlock_bh(&rdev->mgmt_registrations_lock); + spin_unlock_bh(&wdev->mgmt_registrations_lock); if (nlportid && rdev->crit_proto_nlportid == nlportid) { rdev->crit_proto_nlportid = 0; @@ -625,16 +624,15 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid) void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev) { - struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); struct cfg80211_mgmt_registration *reg, *tmp; - spin_lock_bh(&rdev->mgmt_registrations_lock); + spin_lock_bh(&wdev->mgmt_registrations_lock); list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) { list_del(®->list); kfree(reg); } wdev->mgmt_registrations_need_update = 1; - spin_unlock_bh(&rdev->mgmt_registrations_lock); + spin_unlock_bh(&wdev->mgmt_registrations_lock); cfg80211_mgmt_registrations_update(wdev); } @@ -782,7 +780,7 @@ bool cfg80211_rx_mgmt_khz(struct wireless_dev *wdev, int freq, int sig_dbm, data = buf + ieee80211_hdrlen(mgmt->frame_control); data_len = len - ieee80211_hdrlen(mgmt->frame_control); - spin_lock_bh(&rdev->mgmt_registrations_lock); + spin_lock_bh(&wdev->mgmt_registrations_lock); list_for_each_entry(reg, &wdev->mgmt_registrations, list) { if (reg->frame_type != ftype) @@ -806,7 +804,7 @@ bool cfg80211_rx_mgmt_khz(struct wireless_dev *wdev, int freq, int sig_dbm, break; } - spin_unlock_bh(&rdev->mgmt_registrations_lock); + spin_unlock_bh(&wdev->mgmt_registrations_lock); trace_cfg80211_return_bool(result); return result; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 8a7f0c8fba5e9..535e34a84d651 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5,7 +5,7 @@ * Copyright 2006-2010 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2021 Intel Corporation + * Copyright (C) 2018-2020 Intel Corporation */ #include @@ -209,13 +209,9 @@ static int validate_beacon_head(const struct nlattr *attr, unsigned int len = nla_len(attr); const struct element *elem; const struct ieee80211_mgmt *mgmt = (void *)data; + bool s1g_bcn = ieee80211_is_s1g_beacon(mgmt->frame_control); unsigned int fixedlen, hdrlen; - bool s1g_bcn; - if (len < offsetofend(typeof(*mgmt), frame_control)) - goto err; - - s1g_bcn = ieee80211_is_s1g_beacon(mgmt->frame_control); if (s1g_bcn) { fixedlen = offsetof(struct ieee80211_ext, u.s1g_beacon.variable); @@ -475,8 +471,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { .len = IEEE80211_MAX_MESH_ID_LEN }, [NL80211_ATTR_MPATH_NEXT_HOP] = NLA_POLICY_ETH_ADDR_COMPAT, - /* allow 3 for NUL-termination, we used to declare this NLA_STRING */ - [NL80211_ATTR_REG_ALPHA2] = NLA_POLICY_RANGE(NLA_BINARY, 2, 3), + [NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 }, [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED }, [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 }, @@ -2955,15 +2950,6 @@ int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, } else if (attrs[NL80211_ATTR_CHANNEL_WIDTH]) { chandef->width = nla_get_u32(attrs[NL80211_ATTR_CHANNEL_WIDTH]); - if (chandef->chan->band == NL80211_BAND_S1GHZ) { - /* User input error for channel width doesn't match channel */ - if (chandef->width != ieee80211_s1g_channel_width(chandef->chan)) { - NL_SET_ERR_MSG_ATTR(extack, - attrs[NL80211_ATTR_CHANNEL_WIDTH], - "bad channel width"); - return -EINVAL; - } - } if (attrs[NL80211_ATTR_CENTER_FREQ1]) { chandef->center_freq1 = nla_get_u32(attrs[NL80211_ATTR_CENTER_FREQ1]); @@ -3485,7 +3471,6 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag wdev_lock(wdev); switch (wdev->iftype) { case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_P2P_GO: if (wdev->ssid_len && nla_put(msg, NL80211_ATTR_SSID, wdev->ssid_len, wdev->ssid)) goto nla_put_failure_locked; @@ -4636,10 +4621,11 @@ static int nl80211_parse_tx_bitrate_mask(struct genl_info *info, sband->ht_cap.mcs.rx_mask, sizeof(mask->control[i].ht_mcs)); - if (sband->vht_cap.vht_supported) { - vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map); - vht_build_mcs_mask(vht_tx_mcs_map, mask->control[i].vht_mcs); - } + if (!sband->vht_cap.vht_supported) + continue; + + vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map); + vht_build_mcs_mask(vht_tx_mcs_map, mask->control[i].vht_mcs); he_cap = ieee80211_get_he_iftype_cap(sband, wdev->iftype); if (!he_cap) @@ -5334,7 +5320,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) rdev, info->attrs[NL80211_ATTR_UNSOL_BCAST_PROBE_RESP], ¶ms); if (err) - goto out; + return err; } nl80211_calculate_ap_params(¶ms); @@ -11096,23 +11082,18 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, struct cfg80211_bitrate_mask mask; struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; - struct wireless_dev *wdev = dev->ieee80211_ptr; int err; if (!rdev->ops->set_bitrate_mask) return -EOPNOTSUPP; - wdev_lock(wdev); err = nl80211_parse_tx_bitrate_mask(info, info->attrs, NL80211_ATTR_TX_RATES, &mask, dev); if (err) - goto out; + return err; - err = rdev_set_bitrate_mask(rdev, dev, NULL, &mask); -out: - wdev_unlock(wdev); - return err; + return rdev_set_bitrate_mask(rdev, dev, NULL, &mask); } static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info) @@ -12946,9 +12927,6 @@ static int handle_nan_filter(struct nlattr *attr_filter, i = 0; nla_for_each_nested(attr, attr_filter, rem) { filter[i].filter = nla_memdup(attr, GFP_KERNEL); - if (!filter[i].filter) - goto err; - filter[i].len = nla_len(attr); i++; } @@ -12961,15 +12939,6 @@ static int handle_nan_filter(struct nlattr *attr_filter, } return 0; - -err: - i = 0; - nla_for_each_nested(attr, attr_filter, rem) { - kfree(filter[i].filter); - i++; - } - kfree(filter); - return -ENOMEM; } static int nl80211_nan_add_func(struct sk_buff *skb, @@ -17143,8 +17112,7 @@ void cfg80211_ch_switch_notify(struct net_device *dev, wdev->chandef = *chandef; wdev->preset_chandef = *chandef; - if ((wdev->iftype == NL80211_IFTYPE_STATION || - wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) && + if (wdev->iftype == NL80211_IFTYPE_STATION && !WARN_ON(!wdev->current_bss)) cfg80211_update_assoc_bss_entry(wdev, chandef->chan); diff --git a/net/wireless/pmsr.c b/net/wireless/pmsr.c index a817d8e3e4b36..a95c79d183492 100644 --- a/net/wireless/pmsr.c +++ b/net/wireless/pmsr.c @@ -324,7 +324,6 @@ void cfg80211_pmsr_complete(struct wireless_dev *wdev, gfp_t gfp) { struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); - struct cfg80211_pmsr_request *tmp, *prev, *to_free = NULL; struct sk_buff *msg; void *hdr; @@ -355,20 +354,9 @@ void cfg80211_pmsr_complete(struct wireless_dev *wdev, nlmsg_free(msg); free_request: spin_lock_bh(&wdev->pmsr_lock); - /* - * cfg80211_pmsr_process_abort() may have already moved this request - * to the free list, and will free it later. In this case, don't free - * it here. - */ - list_for_each_entry_safe(tmp, prev, &wdev->pmsr_list, list) { - if (tmp == req) { - list_del(&req->list); - to_free = req; - break; - } - } + list_del(&req->list); spin_unlock_bh(&wdev->pmsr_lock); - kfree(to_free); + kfree(req); } EXPORT_SYMBOL_GPL(cfg80211_pmsr_complete); diff --git a/net/wireless/reg.c b/net/wireless/reg.c index a1e64d967bd38..a04fdfb35f070 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -787,8 +787,6 @@ static int __init load_builtin_regdb_keys(void) return 0; } -MODULE_FIRMWARE("regulatory.db.p7s"); - static bool regdb_has_valid_signature(const u8 *data, unsigned int size) { const struct firmware *sig; @@ -1060,12 +1058,8 @@ static void regdb_fw_cb(const struct firmware *fw, void *context) release_firmware(fw); } -MODULE_FIRMWARE("regulatory.db"); - static int query_regdb_file(const char *alpha2) { - int err; - ASSERT_RTNL(); if (regdb) @@ -1075,13 +1069,9 @@ static int query_regdb_file(const char *alpha2) if (!alpha2) return -ENOMEM; - err = request_firmware_nowait(THIS_MODULE, true, "regulatory.db", - ®_pdev->dev, GFP_KERNEL, - (void *)alpha2, regdb_fw_cb); - if (err) - kfree(alpha2); - - return err; + return request_firmware_nowait(THIS_MODULE, true, "regulatory.db", + ®_pdev->dev, GFP_KERNEL, + (void *)alpha2, regdb_fw_cb); } int reg_reload_regdb(void) @@ -4011,7 +4001,6 @@ void wiphy_regulatory_register(struct wiphy *wiphy) wiphy_update_regulatory(wiphy, lr->initiator); wiphy_all_share_dfs_chan_state(wiphy); - reg_process_self_managed_hints(); } void wiphy_regulatory_deregister(struct wiphy *wiphy) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index d09dabae56271..3409f37d838b3 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -143,12 +143,18 @@ static inline void bss_ref_get(struct cfg80211_registered_device *rdev, lockdep_assert_held(&rdev->bss_lock); bss->refcount++; - - if (bss->pub.hidden_beacon_bss) - bss_from_pub(bss->pub.hidden_beacon_bss)->refcount++; - - if (bss->pub.transmitted_bss) - bss_from_pub(bss->pub.transmitted_bss)->refcount++; + if (bss->pub.hidden_beacon_bss) { + bss = container_of(bss->pub.hidden_beacon_bss, + struct cfg80211_internal_bss, + pub); + bss->refcount++; + } + if (bss->pub.transmitted_bss) { + bss = container_of(bss->pub.transmitted_bss, + struct cfg80211_internal_bss, + pub); + bss->refcount++; + } } static inline void bss_ref_put(struct cfg80211_registered_device *rdev, @@ -298,8 +304,7 @@ static size_t cfg80211_gen_new_ie(const u8 *ie, size_t ielen, tmp_old = cfg80211_find_ie(WLAN_EID_SSID, ie, ielen); tmp_old = (tmp_old) ? tmp_old + tmp_old[1] + 2 : ie; - while (tmp_old + 2 - ie <= ielen && - tmp_old + tmp_old[1] + 2 - ie <= ielen) { + while (tmp_old + tmp_old[1] + 2 - ie <= ielen) { if (tmp_old[0] == 0) { tmp_old++; continue; @@ -330,8 +335,7 @@ static size_t cfg80211_gen_new_ie(const u8 *ie, size_t ielen, * determine if they are the same ie. */ if (tmp_old[0] == WLAN_EID_VENDOR_SPECIFIC) { - if (tmp_old[1] >= 5 && tmp[1] >= 5 && - !memcmp(tmp_old + 2, tmp + 2, 5)) { + if (!memcmp(tmp_old + 2, tmp + 2, 5)) { /* same vendor ie, copy from * subelement */ @@ -360,8 +364,7 @@ static size_t cfg80211_gen_new_ie(const u8 *ie, size_t ielen, * copied to new ie, skip ssid, capability, bssid-index ie */ tmp_new = sub_copy; - while (tmp_new + 2 - sub_copy <= subie_len && - tmp_new + tmp_new[1] + 2 - sub_copy <= subie_len) { + while (tmp_new + tmp_new[1] + 2 - sub_copy <= subie_len) { if (!(tmp_new[0] == WLAN_EID_NON_TX_BSSID_CAP || tmp_new[0] == WLAN_EID_SSID)) { memcpy(pos, tmp_new, tmp_new[1] + 2); @@ -415,26 +418,14 @@ cfg80211_add_nontrans_list(struct cfg80211_bss *trans_bss, } ssid_len = ssid[1]; ssid = ssid + 2; + rcu_read_unlock(); /* check if nontrans_bss is in the list */ list_for_each_entry(bss, &trans_bss->nontrans_list, nontrans_list) { - if (is_bss(bss, nontrans_bss->bssid, ssid, ssid_len)) { - rcu_read_unlock(); + if (is_bss(bss, nontrans_bss->bssid, ssid, ssid_len)) return 0; - } } - rcu_read_unlock(); - - /* - * This is a bit weird - it's not on the list, but already on another - * one! The only way that could happen is if there's some BSSID/SSID - * shared by multiple APs in their multi-BSSID profiles, potentially - * with hidden SSID mixed in ... ignore it. - */ - if (!list_empty(&nontrans_bss->nontrans_list)) - return -EINVAL; - /* add to the list */ list_add_tail(&nontrans_bss->nontrans_list, &trans_bss->nontrans_list); return 0; @@ -708,12 +699,8 @@ static bool cfg80211_find_ssid_match(struct cfg80211_colocated_ap *ap, for (i = 0; i < request->n_ssids; i++) { /* wildcard ssid in the scan request */ - if (!request->ssids[i].ssid_len) { - if (ap->multi_bss && !ap->transmitted_bssid) - continue; - + if (!request->ssids[i].ssid_len) return true; - } if (ap->ssid_len && ap->ssid_len == request->ssids[i].ssid_len) { @@ -840,9 +827,6 @@ static int cfg80211_scan_6ghz(struct cfg80211_registered_device *rdev) !cfg80211_find_ssid_match(ap, request)) continue; - if (!request->n_ssids && ap->multi_bss && !ap->transmitted_bssid) - continue; - cfg80211_scan_req_add_chan(request, chan, true); memcpy(scan_6ghz_params->bssid, ap->bssid, ETH_ALEN); scan_6ghz_params->short_ssid = ap->short_ssid; @@ -1603,23 +1587,6 @@ struct cfg80211_non_tx_bss { u8 bssid_index; }; -static void cfg80211_update_hidden_bsses(struct cfg80211_internal_bss *known, - const struct cfg80211_bss_ies *new_ies, - const struct cfg80211_bss_ies *old_ies) -{ - struct cfg80211_internal_bss *bss; - - /* Assign beacon IEs to all sub entries */ - list_for_each_entry(bss, &known->hidden_list, hidden_list) { - const struct cfg80211_bss_ies *ies; - - ies = rcu_access_pointer(bss->pub.beacon_ies); - WARN_ON(ies != old_ies); - - rcu_assign_pointer(bss->pub.beacon_ies, new_ies); - } -} - static bool cfg80211_update_known_bss(struct cfg80211_registered_device *rdev, struct cfg80211_internal_bss *known, @@ -1643,6 +1610,7 @@ cfg80211_update_known_bss(struct cfg80211_registered_device *rdev, kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head); } else if (rcu_access_pointer(new->pub.beacon_ies)) { const struct cfg80211_bss_ies *old; + struct cfg80211_internal_bss *bss; if (known->pub.hidden_beacon_bss && !list_empty(&known->hidden_list)) { @@ -1670,9 +1638,16 @@ cfg80211_update_known_bss(struct cfg80211_registered_device *rdev, if (old == rcu_access_pointer(known->pub.ies)) rcu_assign_pointer(known->pub.ies, new->pub.beacon_ies); - cfg80211_update_hidden_bsses(known, - rcu_access_pointer(new->pub.beacon_ies), - old); + /* Assign beacon IEs to all sub entries */ + list_for_each_entry(bss, &known->hidden_list, hidden_list) { + const struct cfg80211_bss_ies *ies; + + ies = rcu_access_pointer(bss->pub.beacon_ies); + WARN_ON(ies != old); + + rcu_assign_pointer(bss->pub.beacon_ies, + new->pub.beacon_ies); + } if (old) kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head); @@ -1749,8 +1724,6 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev, new->refcount = 1; INIT_LIST_HEAD(&new->hidden_list); INIT_LIST_HEAD(&new->pub.nontrans_list); - /* we'll set this later if it was non-NULL */ - new->pub.transmitted_bss = NULL; if (rcu_access_pointer(tmp->pub.proberesp_ies)) { hidden = rb_find_bss(rdev, tmp, BSS_CMP_HIDE_ZLEN); @@ -1773,14 +1746,14 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev, * be grouped with this beacon for updates ... */ if (!cfg80211_combine_bsses(rdev, new)) { - bss_ref_put(rdev, new); + kfree(new); goto drop; } } if (rdev->bss_entries >= bss_entries_limit && !cfg80211_bss_expire_oldest(rdev)) { - bss_ref_put(rdev, new); + kfree(new); goto drop; } @@ -1985,18 +1958,11 @@ cfg80211_inform_single_bss_data(struct wiphy *wiphy, /* this is a nontransmitting bss, we need to add it to * transmitting bss' list if it is not there */ - spin_lock_bh(&rdev->bss_lock); if (cfg80211_add_nontrans_list(non_tx_data->tx_bss, &res->pub)) { - if (__cfg80211_unlink_bss(rdev, res)) { + if (__cfg80211_unlink_bss(rdev, res)) rdev->bss_generation++; - res = NULL; - } } - spin_unlock_bh(&rdev->bss_lock); - - if (!res) - return NULL; } trace_cfg80211_return_bss(&res->pub); @@ -2115,8 +2081,6 @@ static void cfg80211_parse_mbssid_data(struct wiphy *wiphy, for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, ie, ielen) { if (elem->datalen < 4) continue; - if (elem->data[0] < 1 || (int)elem->data[0] > 8) - continue; for_each_element(sub, elem->data + 1, elem->datalen - 1) { u8 profile_len; @@ -2252,7 +2216,7 @@ cfg80211_update_notlisted_nontrans(struct wiphy *wiphy, size_t new_ie_len; struct cfg80211_bss_ies *new_ies; const struct cfg80211_bss_ies *old; - size_t cpy_len; + u8 cpy_len; lockdep_assert_held(&wiphy_to_rdev(wiphy)->bss_lock); @@ -2319,8 +2283,6 @@ cfg80211_update_notlisted_nontrans(struct wiphy *wiphy, } else { old = rcu_access_pointer(nontrans_bss->beacon_ies); rcu_assign_pointer(nontrans_bss->beacon_ies, new_ies); - cfg80211_update_hidden_bsses(bss_from_pub(nontrans_bss), - new_ies, old); rcu_assign_pointer(nontrans_bss->ies, new_ies); if (old) kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head); @@ -2389,16 +2351,14 @@ cfg80211_inform_single_bss_frame_data(struct wiphy *wiphy, return NULL; if (ext) { - const struct ieee80211_s1g_bcn_compat_ie *compat; - const struct element *elem; + struct ieee80211_s1g_bcn_compat_ie *compat; + u8 *ie; - elem = cfg80211_find_elem(WLAN_EID_S1G_BCN_COMPAT, - variable, ielen); - if (!elem) - return NULL; - if (elem->datalen < sizeof(*compat)) + ie = (void *)cfg80211_find_ie(WLAN_EID_S1G_BCN_COMPAT, + variable, ielen); + if (!ie) return NULL; - compat = (void *)elem->data; + compat = (void *)(ie + 2); bssid = ext->u.s1g_beacon.sa; capability = le16_to_cpu(compat->compat_info); beacon_int = le16_to_cpu(compat->beacon_int); @@ -2467,15 +2427,10 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy, const struct cfg80211_bss_ies *ies1, *ies2; size_t ielen = len - offsetof(struct ieee80211_mgmt, u.probe_resp.variable); - struct cfg80211_non_tx_bss non_tx_data = {}; + struct cfg80211_non_tx_bss non_tx_data; res = cfg80211_inform_single_bss_frame_data(wiphy, data, mgmt, len, gfp); - - /* don't do any further MBSSID handling for S1G */ - if (ieee80211_is_s1g_beacon(mgmt->frame_control)) - return res; - if (!res || !wiphy->support_mbssid || !cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen)) return res; diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 060e365c8259b..38df713f2e2ed 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -530,7 +530,7 @@ static int cfg80211_sme_connect(struct wireless_dev *wdev, cfg80211_sme_free(wdev); } - if (wdev->conn) + if (WARN_ON(wdev->conn)) return -EINPROGRESS; wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL); diff --git a/net/wireless/util.c b/net/wireless/util.c index 4b32e85c2d9a1..e4247c3543566 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -541,7 +541,7 @@ EXPORT_SYMBOL(ieee80211_get_mesh_hdrlen); int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr, const u8 *addr, enum nl80211_iftype iftype, - u8 data_offset, bool is_amsdu) + u8 data_offset) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct { @@ -629,7 +629,7 @@ int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr, skb_copy_bits(skb, hdrlen, &payload, sizeof(payload)); tmp.h_proto = payload.proto; - if (likely((!is_amsdu && ether_addr_equal(payload.hdr, rfc1042_header) && + if (likely((ether_addr_equal(payload.hdr, rfc1042_header) && tmp.h_proto != htons(ETH_P_AARP) && tmp.h_proto != htons(ETH_P_IPX)) || ether_addr_equal(payload.hdr, bridge_tunnel_header))) @@ -771,9 +771,6 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, remaining = skb->len - offset; if (subframe_len > remaining) goto purge; - /* mitigate A-MSDU aggregation injection attacks */ - if (ether_addr_equal(eth.h_dest, rfc1042_header)) - goto purge; offset += sizeof(struct ethhdr); last = remaining <= subframe_len + padding; @@ -1028,14 +1025,14 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, !(rdev->wiphy.interface_modes & (1 << ntype))) return -EOPNOTSUPP; - if (ntype != otype) { - /* if it's part of a bridge, reject changing type to station/ibss */ - if (netif_is_bridge_port(dev) && - (ntype == NL80211_IFTYPE_ADHOC || - ntype == NL80211_IFTYPE_STATION || - ntype == NL80211_IFTYPE_P2P_CLIENT)) - return -EBUSY; + /* if it's part of a bridge, reject changing type to station/ibss */ + if (netif_is_bridge_port(dev) && + (ntype == NL80211_IFTYPE_ADHOC || + ntype == NL80211_IFTYPE_STATION || + ntype == NL80211_IFTYPE_P2P_CLIENT)) + return -EBUSY; + if (ntype != otype) { dev->ieee80211_ptr->use_4addr = false; dev->ieee80211_ptr->mesh_id_up_len = 0; wdev_lock(dev->ieee80211_ptr); @@ -1044,7 +1041,6 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, switch (otype) { case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_P2P_GO: cfg80211_stop_ap(rdev, dev, true); break; case NL80211_IFTYPE_ADHOC: @@ -1060,9 +1056,6 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, case NL80211_IFTYPE_MESH_POINT: /* mesh should be handled? */ break; - case NL80211_IFTYPE_OCB: - cfg80211_leave_ocb(rdev, dev); - break; default: break; } diff --git a/net/wireless/wext-core.c b/net/wireless/wext-core.c index 76a80a41615be..69102fda9ebd4 100644 --- a/net/wireless/wext-core.c +++ b/net/wireless/wext-core.c @@ -896,9 +896,8 @@ static int ioctl_standard_iw_point(struct iw_point *iwp, unsigned int cmd, int call_commit_handler(struct net_device *dev) { #ifdef CONFIG_WIRELESS_EXT - if (netif_running(dev) && - dev->wireless_handlers && - dev->wireless_handlers->standard[0]) + if ((netif_running(dev)) && + (dev->wireless_handlers->standard[0] != NULL)) /* Call the commit handler on the driver */ return dev->wireless_handlers->standard[0](dev, NULL, NULL, NULL); diff --git a/net/wireless/wext-spy.c b/net/wireless/wext-spy.c index b379a03716539..33bef22e44e95 100644 --- a/net/wireless/wext-spy.c +++ b/net/wireless/wext-spy.c @@ -120,8 +120,8 @@ int iw_handler_set_thrspy(struct net_device * dev, return -EOPNOTSUPP; /* Just do it */ - spydata->spy_thr_low = threshold->low; - spydata->spy_thr_high = threshold->high; + memcpy(&(spydata->spy_thr_low), &(threshold->low), + 2 * sizeof(struct iw_quality)); /* Clear flag */ memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under)); @@ -147,8 +147,8 @@ int iw_handler_get_thrspy(struct net_device * dev, return -EOPNOTSUPP; /* Just do it */ - threshold->low = spydata->spy_thr_low; - threshold->high = spydata->spy_thr_high; + memcpy(&(threshold->low), &(spydata->spy_thr_low), + 2 * sizeof(struct iw_quality)); return 0; } @@ -173,10 +173,10 @@ static void iw_send_thrspy_event(struct net_device * dev, memcpy(threshold.addr.sa_data, address, ETH_ALEN); threshold.addr.sa_family = ARPHRD_ETHER; /* Copy stats */ - threshold.qual = *wstats; + memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality)); /* Copy also thresholds */ - threshold.low = spydata->spy_thr_low; - threshold.high = spydata->spy_thr_high; + memcpy(&(threshold.low), &(spydata->spy_thr_low), + 2 * sizeof(struct iw_quality)); /* Send event to user space */ wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold); From d12c1527d29955f74c471640893a77ec8a9c657f Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Fri, 3 Feb 2023 19:27:55 -0600 Subject: [PATCH 23/65] cypress fmac patchset v5.10.9-2022_0909 Signed-off-by: Robert Nelson --- .../net/wireless/broadcom/brcm80211/Kconfig | 22 +- .../broadcom/brcm80211/brcmfmac/Kconfig | 7 + .../broadcom/brcm80211/brcmfmac/Makefile | 2 + .../broadcom/brcm80211/brcmfmac/bcdc.c | 7 +- .../broadcom/brcm80211/brcmfmac/bcmsdh.c | 46 +- .../brcm80211/brcmfmac/bt_shared_sdio.c | 326 +++ .../brcm80211/brcmfmac/bt_shared_sdio.h | 43 + .../broadcom/brcm80211/brcmfmac/bus.h | 30 +- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 2223 ++++++++++++++--- .../broadcom/brcm80211/brcmfmac/cfg80211.h | 49 +- .../broadcom/brcm80211/brcmfmac/chip.c | 376 ++- .../broadcom/brcm80211/brcmfmac/chip.h | 43 +- .../broadcom/brcm80211/brcmfmac/common.c | 105 +- .../broadcom/brcm80211/brcmfmac/common.h | 6 + .../broadcom/brcm80211/brcmfmac/core.c | 318 ++- .../broadcom/brcm80211/brcmfmac/core.h | 29 +- .../broadcom/brcm80211/brcmfmac/debug.c | 83 + .../broadcom/brcm80211/brcmfmac/debug.h | 25 + .../broadcom/brcm80211/brcmfmac/feature.c | 13 +- .../broadcom/brcm80211/brcmfmac/feature.h | 10 +- .../broadcom/brcm80211/brcmfmac/firmware.c | 22 +- .../broadcom/brcm80211/brcmfmac/firmware.h | 15 +- .../broadcom/brcm80211/brcmfmac/flowring.c | 5 +- .../broadcom/brcm80211/brcmfmac/fweh.c | 28 +- .../broadcom/brcm80211/brcmfmac/fweh.h | 32 +- .../broadcom/brcm80211/brcmfmac/fwil.h | 4 + .../broadcom/brcm80211/brcmfmac/fwil_types.h | 82 +- .../broadcom/brcm80211/brcmfmac/fwsignal.c | 65 +- .../broadcom/brcm80211/brcmfmac/fwsignal.h | 3 +- .../broadcom/brcm80211/brcmfmac/msgbuf.c | 124 +- .../broadcom/brcm80211/brcmfmac/msgbuf.h | 1 + .../wireless/broadcom/brcm80211/brcmfmac/of.c | 14 +- .../broadcom/brcm80211/brcmfmac/p2p.c | 195 +- .../broadcom/brcm80211/brcmfmac/p2p.h | 7 +- .../broadcom/brcm80211/brcmfmac/pcie.c | 944 ++++++- .../broadcom/brcm80211/brcmfmac/pcie.h | 2 +- .../broadcom/brcm80211/brcmfmac/pno.c | 12 +- .../broadcom/brcm80211/brcmfmac/sdio.c | 597 ++++- .../broadcom/brcm80211/brcmfmac/sdio.h | 114 + .../broadcom/brcm80211/brcmfmac/trxhdr.h | 38 + .../broadcom/brcm80211/brcmfmac/usb.c | 42 +- .../broadcom/brcm80211/brcmfmac/vendor.c | 131 + .../broadcom/brcm80211/brcmfmac/vendor.h | 20 + .../broadcom/brcm80211/brcmutil/d11.c | 19 +- .../broadcom/brcm80211/include/brcm_hw_ids.h | 17 +- .../broadcom/brcm80211/include/brcmu_d11.h | 15 +- .../broadcom/brcm80211/include/brcmu_utils.h | 13 + .../broadcom/brcm80211/include/brcmu_wifi.h | 17 +- .../broadcom/brcm80211/include/chipcommon.h | 195 +- include/linux/mmc/sdio_ids.h | 10 +- include/net/cfg80211.h | 7 + include/uapi/linux/nl80211.h | 6 +- net/wireless/Makefile | 2 +- net/wireless/nl80211.c | 10 +- net/wireless/sme.c | 2 + 55 files changed, 5967 insertions(+), 606 deletions(-) create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/bt_shared_sdio.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/bt_shared_sdio.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/trxhdr.h diff --git a/drivers/net/wireless/broadcom/brcm80211/Kconfig b/drivers/net/wireless/broadcom/brcm80211/Kconfig index 5bf2318763c55..b520d46ffe5da 100644 --- a/drivers/net/wireless/broadcom/brcm80211/Kconfig +++ b/drivers/net/wireless/broadcom/brcm80211/Kconfig @@ -34,6 +34,26 @@ config BRCM_TRACING config BRCMDBG bool "Broadcom driver debug functions" depends on BRCMSMAC || BRCMFMAC - select WANT_DEV_COREDUMP if BRCMFMAC + select WANT_DEV_COREDUMP help Selecting this enables additional code for debug purposes. + +config BRCMFMAC_PCIE_BARWIN_SZ + bool "Custom PCIE BAR window size support for FullMAC driver" + depends on BRCMFMAC + depends on PCI + default n + help + If you say Y here, the FMAC driver will use custom PCIE BAR + window size. Say Y to allow developers to use custom PCIE + BAR window size when HOST PCIE IP can support less then 4MB + BAR window. + +config BRCMFMAC_BT_SHARED_SDIO + bool "FMAC shares SDIO bus to Bluetooth" + depends on BRCMFMAC + depends on BRCMFMAC_SDIO + default n + help + Selecting this to enables sharing the SDIO bus interface between + Cypress BT and WiFi host drivers. diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Kconfig b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Kconfig index 32794c1eca231..48b5bccd54748 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Kconfig +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Kconfig @@ -48,3 +48,10 @@ config BRCMFMAC_PCIE IEEE802.11ac embedded FullMAC WLAN driver. Say Y if you want to use the driver for an PCIE wireless card. +config BRCMFMAC_BT_SHARED_SDIO + bool "FMAC shares SDIO bus to Bluetooth" + depends on BRCMFMAC_SDIO + default n + help + This option enables the feautre of sharing the SDIO bus interface + between Cypress BT and WiFi host drivers. diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile index 9b15bc3f60548..fa5b4bdf12f49 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile @@ -46,3 +46,5 @@ brcmfmac-$(CONFIG_OF) += \ of.o brcmfmac-$(CONFIG_DMI) += \ dmi.o +brcmfmac-${CONFIG_BRCMFMAC_BT_SHARED_SDIO} += \ + bt_shared_sdio.o diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c index 3984fd7d918e1..4fefd8587e5c6 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c @@ -87,6 +87,8 @@ struct brcmf_proto_bcdc_header { * plus any space that might be needed * for bus alignment padding. */ +#define ROUND_UP_MARGIN 2048 + struct brcmf_bcdc { u16 reqid; u8 bus_header[BUS_HEADER_LEN]; @@ -368,8 +370,7 @@ brcmf_proto_bcdc_txcomplete(struct device *dev, struct sk_buff *txp, /* await txstatus signal for firmware if active */ if (brcmf_fws_fc_active(bcdc->fws)) { - if (!success) - brcmf_fws_bustxfail(bcdc->fws, txp); + brcmf_fws_bustxcomplete(bcdc->fws, txp, success); } else { if (brcmf_proto_bcdc_hdrpull(bus_if->drvr, false, txp, &ifp)) brcmu_pkt_buf_free_skb(txp); @@ -471,7 +472,7 @@ int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) drvr->hdrlen += BCDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES; drvr->bus_if->maxctl = BRCMF_DCMD_MAXLEN + - sizeof(struct brcmf_proto_bcdc_dcmd); + sizeof(struct brcmf_proto_bcdc_dcmd) + ROUND_UP_MARGIN; return 0; fail: diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c index f9ebb98b0e3c7..36abcd3d9e70d 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c @@ -36,6 +36,7 @@ #include "sdio.h" #include "core.h" #include "common.h" +#include "cfg80211.h" #define SDIOH_API_ACCESS_RETRY_LIMIT 2 @@ -43,9 +44,12 @@ #define SDIO_FUNC1_BLOCKSIZE 64 #define SDIO_FUNC2_BLOCKSIZE 512 -#define SDIO_4373_FUNC2_BLOCKSIZE 256 +#define SDIO_4373_FUNC2_BLOCKSIZE 128 #define SDIO_435X_FUNC2_BLOCKSIZE 256 #define SDIO_4329_FUNC2_BLOCKSIZE 128 +#define SDIO_89459_FUNC2_BLOCKSIZE 256 +#define SDIO_CYW55572_FUNC2_BLOCKSIZE 256 + /* Maximum milliseconds to wait for F2 to come up */ #define SDIO_WAIT_F2RDY 3000 @@ -669,7 +673,7 @@ brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, uint dsize; dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size); - pkt = dev_alloc_skb(dsize); + pkt = __dev_alloc_skb(dsize, GFP_KERNEL); if (!pkt) { brcmf_err("dev_alloc_skb failed: len %d\n", dsize); return -EIO; @@ -924,6 +928,15 @@ int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) case SDIO_DEVICE_ID_BROADCOM_4329: f2_blksz = SDIO_4329_FUNC2_BLOCKSIZE; break; + case SDIO_DEVICE_ID_BROADCOM_CYPRESS_89459: + case SDIO_DEVICE_ID_CYPRESS_54590: + case SDIO_DEVICE_ID_CYPRESS_54591: + case SDIO_DEVICE_ID_CYPRESS_54594: + f2_blksz = SDIO_89459_FUNC2_BLOCKSIZE; + break; + case SDIO_DEVICE_ID_CYPRESS_55572: + f2_blksz = SDIO_CYW55572_FUNC2_BLOCKSIZE; + break; default: break; } @@ -969,6 +982,9 @@ int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) #define BRCMF_SDIO_DEVICE(dev_id) \ {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, dev_id)} +#define CYF_SDIO_DEVICE(dev_id) \ + {SDIO_DEVICE(SDIO_VENDOR_ID_CYPRESS, dev_id)} + /* devices we support, null terminated */ static const struct sdio_device_id brcmf_sdmmc_ids[] = { BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43143), @@ -988,9 +1004,15 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = { BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4356), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4359), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43439), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_89359), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_89459), + CYF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_43439), + CYF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_54590), + CYF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_54591), + CYF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_54594), + CYF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_55572), { /* end: all zeroes */ } }; MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); @@ -1059,6 +1081,7 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, dev_set_drvdata(&func->dev, bus_if); dev_set_drvdata(&sdiodev->func1->dev, bus_if); sdiodev->dev = &sdiodev->func1->dev; + dev_set_drvdata(&sdiodev->func2->dev, bus_if); brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_DOWN); @@ -1075,6 +1098,7 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, fail: dev_set_drvdata(&func->dev, NULL); dev_set_drvdata(&sdiodev->func1->dev, NULL); + dev_set_drvdata(&sdiodev->func2->dev, NULL); kfree(sdiodev); kfree(bus_if); return err; @@ -1129,15 +1153,27 @@ static int brcmf_ops_sdio_suspend(struct device *dev) struct brcmf_bus *bus_if; struct brcmf_sdio_dev *sdiodev; mmc_pm_flag_t pm_caps, sdio_flags; + struct brcmf_cfg80211_info *config; + int retry = BRCMF_PM_WAIT_MAXRETRY; int ret = 0; func = container_of(dev, struct sdio_func, dev); + bus_if = dev_get_drvdata(dev); + config = bus_if->drvr->config; + brcmf_dbg(SDIO, "Enter: F%d\n", func->num); + + while (retry && + config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING) { + usleep_range(10000, 20000); + retry--; + } + if (!retry && config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING) + brcmf_err("timed out wait for cfg80211 suspended\n"); + if (func->num != 1) return 0; - - bus_if = dev_get_drvdata(dev); sdiodev = bus_if->bus_priv.sdio; pm_caps = sdio_get_host_pm_caps(func); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bt_shared_sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bt_shared_sdio.c new file mode 100644 index 0000000000000..45e53c6ee97d9 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bt_shared_sdio.c @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: ISC + +/* Copyright 2019, Cypress Semiconductor Corporation or a subsidiary of + * Cypress Semiconductor Corporation. All rights reserved. + * This software, including source code, documentation and related + * materials ("Software"), is owned by Cypress Semiconductor + * Corporation or one of its subsidiaries ("Cypress") and is protected by + * and subject to worldwide patent protection (United States and foreign), + * United States copyright laws and international treaty provisions. + * Therefore, you may use this Software only as provided in the license + * agreement accompanying the software package from which you + * obtained this Software ("EULA"). If no EULA applies, Cypress hereby grants + * you a personal, nonexclusive, non-transferable license to copy, modify, + * and compile the Software source code solely for use in connection with + * Cypress's integrated circuit products. Any reproduction, modification, + * translation, compilation, or representation of this Software except as + * specified above is prohibited without the express written permission of + * Cypress. + * Disclaimer: THIS SOFTWARE IS PROVIDED AS-IS, WITH NO WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, NONINFRINGEMENT, IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. Cypress + * reserves the right to make changes to the Software without notice. Cypress + * does not assume any liability arising out of the application or use of the + * Software or any product or circuit described in the Software. Cypress does + * not authorize its products for use in any products where a malfunction or + * failure of the Cypress product may reasonably be expected to result in + * significant property damage, injury or death ("High Risk Product"). By + * including Cypress's product in a High Risk Product, the manufacturer + * of such system or application assumes all risk of such use and in doing + * so agrees to indemnify Cypress against all liability. + */ + +#include +#include +#include +#include +#include +#include "bus.h" +#include "chipcommon.h" +#include "core.h" +#include "sdio.h" +#include "soc.h" +#include "fwil.h" + +#define SDIOD_ADDR_BOUND 0x1000 +#define SDIOD_ADDR_BOUND_MASK 0xfff + +struct brcmf_bus *g_bus_if; + +enum bus_owner { + WLAN_MODULE = 0, + BT_MODULE +}; + +struct btsdio_info { + u32 bt_buf_reg_addr; + u32 host_ctrl_reg_addr; + u32 bt_ctrl_reg_addr; + u32 bt_buf_addr; + u32 wlan_buf_addr; +}; + +void brcmf_btsdio_int_handler(struct brcmf_bus *bus_if) +{ + struct brcmf_bt_dev *btdev = bus_if->bt_dev; + + if (btdev && btdev->bt_sdio_int_cb) + btdev->bt_sdio_int_cb(btdev->bt_data); +} + +int brcmf_btsdio_init(struct brcmf_bus *bus_if) +{ + if (!bus_if) + return -EINVAL; + + g_bus_if = bus_if; + return 0; +} + +int brcmf_btsdio_attach(struct brcmf_bus *bus_if, void *btdata, + void (*bt_int_fun)(void *data)) +{ + struct brcmf_bt_dev *btdev; + + /* Allocate bt dev */ + btdev = kzalloc(sizeof(*btdev), GFP_ATOMIC); + if (!btdev) + return -ENOMEM; + + btdev->bt_data = btdata; + btdev->bt_sdio_int_cb = bt_int_fun; + bus_if->bt_dev = btdev; + + return 0; +} + +void brcmf_btsdio_detach(struct brcmf_bus *bus_if) +{ + struct brcmf_bt_dev *btdev = bus_if->bt_dev; + + if (!btdev) + return; + + if (btdev->bt_data) + btdev->bt_data = NULL; + if (btdev->bt_sdio_int_cb) + btdev->bt_sdio_int_cb = NULL; + if (bus_if->bt_dev) { + bus_if->bt_dev = NULL; + kfree(btdev); + } +} + +u8 brcmf_btsdio_bus_count(struct brcmf_bus *bus_if) +{ + struct brcmf_bt_dev *btdev = bus_if->bt_dev; + + if (!btdev) + return 0; + + return btdev->use_count; +} + +void *brcmf_bt_sdio_attach(void *btdata, void (*bt_int_fun)(void *data)) +{ + int err; + + if (!g_bus_if) { + brcmf_err("BTSDIO is not initialized\n"); + return NULL; + } + + err = brcmf_btsdio_attach(g_bus_if, btdata, bt_int_fun); + if (err) { + brcmf_err("BTSDIO attach failed, err=%d\n", err); + return NULL; + } + + return (void *)g_bus_if; +} +EXPORT_SYMBOL(brcmf_bt_sdio_attach); + +int brcmf_get_wlan_info(struct brcmf_bus *bus_if, struct btsdio_info *bs_info) +{ + struct brcmf_if *ifp; + + if (!bus_if || !bs_info) + return -EINVAL; + + ifp = bus_if->drvr->iflist[0]; + + bs_info->bt_buf_reg_addr = SI_ENUM_BASE + 0xC00 + + CHIPGCIREGOFFS(gci_input[6]); + bs_info->host_ctrl_reg_addr = SI_ENUM_BASE + 0xC00 + + CHIPGCIREGOFFS(gci_output[3]); + bs_info->bt_ctrl_reg_addr = SI_ENUM_BASE + 0xC00 + + CHIPGCIREGOFFS(gci_input[7]); + brcmf_dbg(INFO, "BT buf reg addr: 0x%x\n", + bs_info->bt_buf_reg_addr); + brcmf_dbg(INFO, "HOST ctrl reg addr: 0x%x\n", + bs_info->host_ctrl_reg_addr); + brcmf_dbg(INFO, "BT ctrl reg addr: 0x%x\n", + bs_info->bt_ctrl_reg_addr); + return 0; +} +EXPORT_SYMBOL(brcmf_get_wlan_info); + +u32 brcmf_bus_reg_read(struct brcmf_bus *bus_if, u32 addr) +{ + struct brcmf_sdio_dev *sdiodev; + int err = 0; + u32 val; + + if (!bus_if) + return -EINVAL; + + sdiodev = bus_if->bus_priv.sdio; + + sdio_claim_host(sdiodev->func1); + val = brcmf_sdiod_readl(sdiodev, addr, &err); + if (err) { + brcmf_err("sdio reg read failed, err=%d\n", err); + sdio_release_host(sdiodev->func1); + return err; + } + sdio_release_host(sdiodev->func1); + + return val; +} +EXPORT_SYMBOL(brcmf_bus_reg_read); + +void brcmf_bus_reg_write(struct brcmf_bus *bus_if, u32 addr, u32 val) +{ + struct brcmf_sdio_dev *sdiodev; + int err = 0; + + if (!bus_if) + return; + + sdiodev = bus_if->bus_priv.sdio; + + sdio_claim_host(sdiodev->func1); + brcmf_sdiod_writel(sdiodev, addr, val, &err); + if (err) + brcmf_err("sdio reg write failed, err=%d\n", err); + sdio_release_host(sdiodev->func1); +} +EXPORT_SYMBOL(brcmf_bus_reg_write); + +int brcmf_membytes(struct brcmf_bus *bus_if, bool set, u32 address, u8 *data, + unsigned int size) +{ + struct brcmf_sdio_dev *sdiodev; + int err = 0; + u32 block1_offset; + u32 block2_addr; + u16 block1_size; + u16 block2_size; + u8 *block2_data; + + if (!bus_if || !data) + return -EINVAL; + + sdiodev = bus_if->bus_priv.sdio; + /* To avoid SDIO access crosses AXI 4k address boundaries crossing */ + if (((address & SDIOD_ADDR_BOUND_MASK) + size) > SDIOD_ADDR_BOUND) { + brcmf_dbg(SDIO, "data cross 4K boundary\n"); + /* The 1st 4k packet */ + block1_offset = address & SDIOD_ADDR_BOUND_MASK; + block1_size = (SDIOD_ADDR_BOUND - block1_offset); + sdio_claim_host(sdiodev->func1); + err = brcmf_sdiod_ramrw(sdiodev, set, address, + data, block1_size); + if (err) { + brcmf_err("sdio memory access failed, err=%d\n", err); + sdio_release_host(sdiodev->func1); + return err; + } + /* The 2nd 4k packet */ + block2_addr = address + block1_size; + block2_size = size - block1_size; + block2_data = data + block1_size; + err = brcmf_sdiod_ramrw(sdiodev, set, block2_addr, + block2_data, block2_size); + if (err) + brcmf_err("sdio memory access failed, err=%d\n", err); + sdio_release_host(sdiodev->func1); + } else { + sdio_claim_host(sdiodev->func1); + err = brcmf_sdiod_ramrw(sdiodev, set, address, data, size); + if (err) + brcmf_err("sdio memory access failed, err=%d\n", err); + sdio_release_host(sdiodev->func1); + } + return err; +} +EXPORT_SYMBOL(brcmf_membytes); + +/* Function to enable the Bus Clock + * This function is not callable from non-sleepable context + */ +int brcmf_bus_clk_enable(struct brcmf_bus *bus_if, enum bus_owner owner) +{ + struct brcmf_sdio_dev *sdiodev; + struct brcmf_bt_dev *btdev; + int err = 0; + + if (!bus_if) + return -EINVAL; + + btdev = bus_if->bt_dev; + sdiodev = bus_if->bus_priv.sdio; + + sdio_claim_host(sdiodev->func1); + btdev->use_count++; + sdio_release_host(sdiodev->func1); + err = brcmf_sdio_sleep(sdiodev->bus, false); + + return err; +} +EXPORT_SYMBOL(brcmf_bus_clk_enable); + +/* Function to disable the Bus Clock + * This function is not callable from non-sleepable context + */ +int brcmf_bus_clk_disable(struct brcmf_bus *bus_if, enum bus_owner owner) +{ + struct brcmf_sdio_dev *sdiodev; + struct brcmf_bt_dev *btdev; + int err = 0; + + if (!bus_if) + return -EINVAL; + + btdev = bus_if->bt_dev; + sdiodev = bus_if->bus_priv.sdio; + + sdio_claim_host(sdiodev->func1); + if (btdev->use_count != 0) + btdev->use_count--; + sdio_release_host(sdiodev->func1); + err = brcmf_sdio_sleep(sdiodev->bus, true); + + return err; +} +EXPORT_SYMBOL(brcmf_bus_clk_disable); + +/* Function to reset bt_use_count counter to zero. + * This function is not callable from non-sleepable context + */ +void brcmf_bus_reset_bt_use_count(struct brcmf_bus *bus_if) +{ + struct brcmf_sdio_dev *sdiodev; + struct brcmf_bt_dev *btdev; + + if (!bus_if) + return; + + btdev = bus_if->bt_dev; + sdiodev = bus_if->bus_priv.sdio; + + sdio_claim_host(sdiodev->func1); + btdev->use_count = 0; + sdio_release_host(sdiodev->func1); +} +EXPORT_SYMBOL(brcmf_bus_reset_bt_use_count); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bt_shared_sdio.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bt_shared_sdio.h new file mode 100644 index 0000000000000..f0e6b38cf77f3 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bt_shared_sdio.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: ISC */ +/* Copyright 2019, Cypress Semiconductor Corporation or a subsidiary of + * Cypress Semiconductor Corporation. All rights reserved. + * This software, including source code, documentation and related + * materials ("Software"), is owned by Cypress Semiconductor + * Corporation or one of its subsidiaries ("Cypress") and is protected by + * and subject to worldwide patent protection (United States and foreign), + * United States copyright laws and international treaty provisions. + * Therefore, you may use this Software only as provided in the license + * agreement accompanying the software package from which you + * obtained this Software ("EULA"). If no EULA applies, Cypress hereby grants + * you a personal, nonexclusive, non-transferable license to copy, modify, + * and compile the Software source code solely for use in connection with + * Cypress's integrated circuit products. Any reproduction, modification, + * translation, compilation, or representation of this Software except as + * specified above is prohibited without the express written permission of + * Cypress. + * Disclaimer: THIS SOFTWARE IS PROVIDED AS-IS, WITH NO WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, NONINFRINGEMENT, IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. Cypress + * reserves the right to make changes to the Software without notice. Cypress + * does not assume any liability arising out of the application or use of the + * Software or any product or circuit described in the Software. Cypress does + * not authorize its products for use in any products where a malfunction or + * failure of the Cypress product may reasonably be expected to result in + * significant property damage, injury or death ("High Risk Product"). By + * including Cypress's product in a High Risk Product, the manufacturer + * of such system or application assumes all risk of such use and in doing + * so agrees to indemnify Cypress against all liability. + */ + +#ifdef CONFIG_BRCMFMAC_BT_SHARED_SDIO +int brcmf_btsdio_init(struct brcmf_bus *bus_if); +void brcmf_btsdio_detach(struct brcmf_bus *bus_if); +void brcmf_btsdio_int_handler(struct brcmf_bus *bus_if); +u8 brcmf_btsdio_bus_count(struct brcmf_bus *bus_if); +#else +static inline +u8 brcmf_btsdio_bus_count(struct brcmf_bus *bus_if) +{ + return 0; +} +#endif /* CONFIG_BRCMFMAC_BT_SHARED_SDIO */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h index 08f9d47f2e5ca..07073221bf117 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h @@ -7,6 +7,7 @@ #define BRCMFMAC_BUS_H #include "debug.h" +#include /* IDs of the 6 default common rings of msgbuf protocol */ #define BRCMF_H2D_MSGRING_CONTROL_SUBMIT 0 @@ -22,6 +23,12 @@ #define BRCMF_NROF_COMMON_MSGRINGS (BRCMF_NROF_H2D_COMMON_MSGRINGS + \ BRCMF_NROF_D2H_COMMON_MSGRINGS) +/* The interval to poll console */ +#define BRCMF_CONSOLE 10 + +/* The maximum console interval value (5 mins) */ +#define MAX_CONSOLE_INTERVAL (5 * 60) + /* The level of bus communication with the dongle */ enum brcmf_bus_state { BRCMF_BUS_DOWN, /* Not ready for frame transfers */ @@ -117,6 +124,19 @@ struct brcmf_bus_stats { atomic_t pktcow_failed; }; +/** + * struct brcmf_bt_dev - bt shared SDIO device. + * + * @ bt_data: bt internal structure data + * @ bt_sdio_int_cb: bt registered interrupt callback function + * @ bt_use_count: Counter that tracks whether BT is using the bus + */ +struct brcmf_bt_dev { + void *bt_data; + void (*bt_sdio_int_cb)(void *data); + u32 use_count; /* Counter for tracking if BT is using the bus */ +}; + /** * struct brcmf_bus - interface structure between common and bus layer * @@ -132,6 +152,7 @@ struct brcmf_bus_stats { * @wowl_supported: is wowl supported by bus driver. * @chiprev: revision of the dongle chip. * @msgbuf: msgbuf protocol parameters provided by bus layer. + * @bt_dev: bt shared SDIO device */ struct brcmf_bus { union { @@ -152,6 +173,12 @@ struct brcmf_bus { const struct brcmf_bus_ops *ops; struct brcmf_bus_msgbuf *msgbuf; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) + bool allow_skborphan; +#endif +#ifdef CONFIG_BRCMFMAC_BT_SHARED_SDIO + struct brcmf_bt_dev *bt_dev; +#endif /* CONFIG_BRCMFMAC_BT_SHARED_SDIO */ }; /* @@ -256,7 +283,7 @@ void brcmf_rx_event(struct device *dev, struct sk_buff *rxp); int brcmf_alloc(struct device *dev, struct brcmf_mp_device *settings); /* Indication from bus module regarding presence/insertion of dongle. */ -int brcmf_attach(struct device *dev); +int brcmf_attach(struct device *dev, bool start_bus); /* Indication from bus module regarding removal/absence of dongle */ void brcmf_detach(struct device *dev); void brcmf_free(struct device *dev); @@ -272,6 +299,7 @@ void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state); s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, u32 len); void brcmf_bus_add_txhdrlen(struct device *dev, uint len); +int brcmf_fwlog_attach(struct device *dev); #ifdef CONFIG_BRCMFMAC_SDIO void brcmf_sdio_exit(void); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 0ee421f30aa24..e0a0a99fd5467 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -39,6 +39,7 @@ #define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */ #define WME_OUI_TYPE 2 #define WPS_OUI_TYPE 4 +#define WFA_OUI_TYPE_MBO_OCE 0x16 #define VS_IE_FIXED_HDR_LEN 6 #define WPA_IE_VERSION_LEN 2 @@ -63,6 +64,9 @@ #define RSN_CAP_MFPC_MASK BIT(7) #define RSN_PMKID_COUNT_LEN 2 +#define DPP_AKM_SUITE_TYPE 2 +#define WLAN_AKM_SUITE_DPP SUITE(WLAN_OUI_WFA, DPP_AKM_SUITE_TYPE) + #define VNDR_IE_CMD_LEN 4 /* length of the set command * string :"add", "del" (+ NUL) */ @@ -75,8 +79,10 @@ #define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */ #define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */ -#define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320 -#define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400 +#define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320 +#define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400 +#define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS_6E 80 +#define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS_6E 130 #define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS 20 #define BRCMF_SCAN_CHANNEL_TIME 40 @@ -85,11 +91,51 @@ #define BRCMF_ND_INFO_TIMEOUT msecs_to_jiffies(2000) -#define BRCMF_PS_MAX_TIMEOUT_MS 2000 +#define MGMT_AUTH_FRAME_DWELL_TIME 4000 +#define MGMT_AUTH_FRAME_WAIT_TIME (MGMT_AUTH_FRAME_DWELL_TIME + 100) + +/* Dump obss definitions */ +#define ACS_MSRMNT_DELAY 80 +#define CHAN_NOISE_DUMMY (-80) +#define OBSS_TOKEN_IDX 15 +#define IBSS_TOKEN_IDX 15 +#define TX_TOKEN_IDX 14 +#define CTG_TOKEN_IDX 13 +#define PKT_TOKEN_IDX 15 +#define IDLE_TOKEN_IDX 12 #define BRCMF_ASSOC_PARAMS_FIXED_SIZE \ (sizeof(struct brcmf_assoc_params_le) - sizeof(u16)) +struct brcmf_dump_survey { + u32 obss; + u32 ibss; + u32 no_ctg; + u32 no_pckt; + u32 tx; + u32 idle; +}; + +struct cca_stats_n_flags { + u32 msrmnt_time; /* Time for Measurement (msec) */ + u32 msrmnt_done; /* flag set when measurement complete */ + char buf[1]; +}; + +struct cca_msrmnt_query { + u32 msrmnt_query; + u32 time_req; +}; + +/* algo bit vector */ +#define KEY_ALGO_MASK(_algo) (1 << (_algo)) +/* version of the wl_wsec_info structure */ +#define WL_WSEC_INFO_VERSION 0x01 + +/* start enum value for BSS properties */ +#define WL_WSEC_INFO_BSS_BASE 0x0100 +#define WL_WSEC_INFO_BSS_ALGOS (WL_WSEC_INFO_BSS_BASE + 6) + static bool check_vif_up(struct brcmf_cfg80211_vif *vif) { if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) { @@ -144,6 +190,14 @@ static struct ieee80211_rate __wl_rates[] = { .max_power = 30, \ } +#define CHAN6G(_channel) { \ + .band = NL80211_BAND_6GHZ, \ + .center_freq = 5950 + (5 * (_channel)), \ + .hw_value = (_channel), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + static struct ieee80211_channel __wl_2ghz_channels[] = { CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427), CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447), @@ -160,6 +214,23 @@ static struct ieee80211_channel __wl_5ghz_channels[] = { CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165) }; +static struct ieee80211_channel __wl_6ghz_channels[] = { + CHAN6G(1), CHAN6G(5), CHAN6G(9), CHAN6G(13), CHAN6G(17), + CHAN6G(21), CHAN6G(25), CHAN6G(29), CHAN6G(33), CHAN6G(37), + CHAN6G(41), CHAN6G(45), CHAN6G(49), CHAN6G(53), CHAN6G(57), + CHAN6G(61), CHAN6G(65), CHAN6G(69), CHAN6G(73), CHAN6G(77), + CHAN6G(81), CHAN6G(85), CHAN6G(89), CHAN6G(93), CHAN6G(97), + CHAN6G(101), CHAN6G(105), CHAN6G(109), CHAN6G(113), CHAN6G(117), + CHAN6G(121), CHAN6G(125), CHAN6G(129), CHAN6G(133), CHAN6G(137), + CHAN6G(141), CHAN6G(145), CHAN6G(149), CHAN6G(153), CHAN6G(157), + CHAN6G(161), CHAN6G(165), CHAN6G(169), CHAN6G(173), CHAN6G(177), + CHAN6G(181), CHAN6G(185), CHAN6G(189), CHAN6G(193), CHAN6G(197), + CHAN6G(201), CHAN6G(205), CHAN6G(209), CHAN6G(213), CHAN6G(217), + CHAN6G(221), CHAN6G(225), CHAN6G(229), CHAN6G(233) +}; + +struct ieee80211_sband_iftype_data sdata[NUM_NL80211_BANDS]; + /* Band templates duplicated per wiphy. The channel info * above is added to the band during setup. */ @@ -175,6 +246,12 @@ static const struct ieee80211_supported_band __wl_band_5ghz = { .n_bitrates = wl_a_rates_size, }; +static struct ieee80211_supported_band __wl_band_6ghz = { + .band = NL80211_BAND_6GHZ, + .bitrates = wl_a_rates, + .n_bitrates = wl_a_rates_size, +}; + /* This is to override regulatory domains defined in cfg80211 module (reg.c) * By default world regulatory domain defined in reg.c puts the flags * NL80211_RRF_NO_IR for 5GHz channels (for * 36..48 and 149..165). @@ -183,7 +260,7 @@ static const struct ieee80211_supported_band __wl_band_5ghz = { * domain are to be done here. */ static const struct ieee80211_regdomain brcmf_regdom = { - .n_reg_rules = 4, + .n_reg_rules = 5, .alpha2 = "99", .reg_rules = { /* IEEE 802.11b/g, channels 1..11 */ @@ -196,22 +273,31 @@ static const struct ieee80211_regdomain brcmf_regdom = { /* IEEE 802.11a, channel 36..64 */ REG_RULE(5150-10, 5350+10, 160, 6, 20, 0), /* IEEE 802.11a, channel 100..165 */ - REG_RULE(5470-10, 5850+10, 160, 6, 20, 0), } + REG_RULE(5470-10, 5850+10, 160, 6, 20, 0), + /* IEEE 802.11ax, 6E */ + REG_RULE(5935-10, 7115+10, 160, 6, 20, 0), + } }; /* Note: brcmf_cipher_suites is an array of int defining which cipher suites * are supported. A pointer to this array and the number of entries is passed * on to upper layers. AES_CMAC defines whether or not the driver supports MFP. * So the cipher suite AES_CMAC has to be the last one in the array, and when - * device does not support MFP then the number of suites will be decreased by 1 + * device does not support MFP then the number of suites will be decreased by 4 */ static const u32 brcmf_cipher_suites[] = { WLAN_CIPHER_SUITE_WEP40, WLAN_CIPHER_SUITE_WEP104, WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_CCMP, + WLAN_CIPHER_SUITE_CCMP_256, + WLAN_CIPHER_SUITE_GCMP, + WLAN_CIPHER_SUITE_GCMP_256, /* Keep as last entry: */ - WLAN_CIPHER_SUITE_AES_CMAC + WLAN_CIPHER_SUITE_AES_CMAC, + WLAN_CIPHER_SUITE_BIP_CMAC_256, + WLAN_CIPHER_SUITE_BIP_GMAC_128, + WLAN_CIPHER_SUITE_BIP_GMAC_256 }; /* Vendor specific ie. id = 221, oui and type defines exact ie */ @@ -233,6 +319,152 @@ struct parsed_vndr_ies { struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT]; }; +#define WLC_E_IF_ROLE_STA 0 /* Infra STA */ +#define WLC_E_IF_ROLE_AP 1 /* Access Point */ + +#define WL_INTERFACE_CREATE_VER_1 1 +#define WL_INTERFACE_CREATE_VER_2 2 +#define WL_INTERFACE_CREATE_VER_3 3 +#define WL_INTERFACE_CREATE_VER_MAX WL_INTERFACE_CREATE_VER_3 + +#define WL_INTERFACE_MAC_DONT_USE 0x0 +#define WL_INTERFACE_MAC_USE 0x2 + +#define WL_INTERFACE_CREATE_STA 0x0 +#define WL_INTERFACE_CREATE_AP 0x1 + +struct wl_interface_create_v1 { + u16 ver; /* structure version */ + u32 flags; /* flags for operation */ + u8 mac_addr[ETH_ALEN]; /* MAC address */ + u32 wlc_index; /* optional for wlc index */ +}; + +struct wl_interface_create_v2 { + u16 ver; /* structure version */ + u8 pad1[2]; + u32 flags; /* flags for operation */ + u8 mac_addr[ETH_ALEN]; /* MAC address */ + u8 iftype; /* type of interface created */ + u8 pad2; + u32 wlc_index; /* optional for wlc index */ +}; + +struct wl_interface_create_v3 { + u16 ver; /* structure version */ + u16 len; /* length of structure + data */ + u16 fixed_len; /* length of structure */ + u8 iftype; /* type of interface created */ + u8 wlc_index; /* optional for wlc index */ + u32 flags; /* flags for operation */ + u8 mac_addr[ETH_ALEN]; /* MAC address */ + u8 bssid[ETH_ALEN]; /* optional for BSSID */ + u8 if_index; /* interface index request */ + u8 pad[3]; + u8 data[]; /* Optional for specific data */ +}; + +/* tlv used to return wl_wsec_info properties */ +struct wl_wsec_info_tlv { + u16 type; + u16 len; /* data length */ + u8 data[1]; /* data follows */ +}; + +/* input/output data type for wsec_info iovar */ +struct wl_wsec_info { + u8 version; /* structure version */ + u8 pad[2]; + u8 num_tlvs; + struct wl_wsec_info_tlv tlvs[1]; /* tlv data follows */ +}; + +struct bcm_xtlv { + u16 id; + u16 len; + u8 data[1]; +}; + +static int brcmf_setup_wiphybands(struct brcmf_cfg80211_info *cfg); +static bool +wl_cfgoce_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len, + const u8 *oui, u32 oui_len, u8 type); + +/* Check whether the given IE looks like WFA OCE IE. */ +#define wl_cfgoce_is_oce_ie(ie, tlvs, len) \ + wl_cfgoce_has_ie(ie, tlvs, len, \ + (const u8 *)WFA_OUI, TLV_OUI_LEN, WFA_OUI_TYPE_MBO_OCE) + +static s32 +wl_set_wsec_info_algos(struct brcmf_if *ifp, u32 algos, u32 mask) +{ + struct brcmf_pub *drvr = ifp->drvr; + s32 err = 0; + struct wl_wsec_info *wsec_info; + struct bcm_xtlv *wsec_info_tlv; + u16 tlv_data_len; + u8 tlv_data[8]; + u32 param_len; + u8 *buf; + + brcmf_dbg(TRACE, "Enter\n"); + + buf = kzalloc(sizeof(struct wl_wsec_info) + sizeof(tlv_data), GFP_KERNEL); + if (!buf) { + bphy_err(drvr, "unable to allocate.\n"); + return -ENOMEM; + } + + wsec_info = (struct wl_wsec_info *)buf; + wsec_info->version = WL_WSEC_INFO_VERSION; + wsec_info_tlv = (struct bcm_xtlv *)(buf + offsetof(struct wl_wsec_info, tlvs)); + + wsec_info->num_tlvs++; + tlv_data_len = sizeof(tlv_data); + memcpy(tlv_data, &algos, sizeof(algos)); + memcpy(tlv_data + sizeof(algos), &mask, sizeof(mask)); + + wsec_info_tlv->id = cpu_to_le16(WL_WSEC_INFO_BSS_ALGOS); + wsec_info_tlv->len = cpu_to_le16(tlv_data_len); + memcpy(wsec_info_tlv->data, tlv_data, tlv_data_len); + + param_len = offsetof(struct wl_wsec_info, tlvs) + + offsetof(struct wl_wsec_info_tlv, data) + tlv_data_len; + + err = brcmf_fil_bsscfg_data_set(ifp, "wsec_info", buf, param_len); + if (err) + brcmf_err("set wsec_info_error:%d\n", err); + + kfree(buf); + return err; +} + +/* Is any of the tlvs the expected entry? If + * not update the tlvs buffer pointer/length. + */ +static bool +wl_cfgoce_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len, + const u8 *oui, u32 oui_len, u8 type) +{ + /* If the contents match the OUI and the type */ + if (ie[TLV_LEN_OFF] >= oui_len + 1 && + !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) && + type == ie[TLV_BODY_OFF + oui_len]) { + return true; + } + + if (!tlvs) + return false; + /* point to the next ie */ + ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN; + /* calculate the length of the rest of the buffer */ + *tlvs_len -= (int)(ie - *tlvs); + /* update the pointer to the start of the buffer */ + *tlvs = ie; + + return false; +} + static u8 nl80211_band_to_fwil(enum nl80211_band band) { switch (band) { @@ -240,6 +472,8 @@ static u8 nl80211_band_to_fwil(enum nl80211_band band) return WLC_BAND_2G; case NL80211_BAND_5GHZ: return WLC_BAND_5G; + case NL80211_BAND_6GHZ: + return WLC_BAND_6G; default: WARN_ON(1); break; @@ -313,6 +547,9 @@ static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf, case NL80211_BAND_5GHZ: ch_inf.band = BRCMU_CHAN_BAND_5G; break; + case NL80211_BAND_6GHZ: + ch_inf.band = BRCMU_CHAN_BAND_6G; + break; case NL80211_BAND_60GHZ: default: WARN_ON_ONCE(1); @@ -328,6 +565,20 @@ u16 channel_to_chanspec(struct brcmu_d11inf *d11inf, { struct brcmu_chan ch_inf; + switch (ch->band) { + case NL80211_BAND_2GHZ: + ch_inf.band = BRCMU_CHAN_BAND_2G; + break; + case NL80211_BAND_5GHZ: + ch_inf.band = BRCMU_CHAN_BAND_5G; + break; + case NL80211_BAND_6GHZ: + ch_inf.band = BRCMU_CHAN_BAND_6G; + break; + case NL80211_BAND_60GHZ: + default: + WARN_ON_ONCE(1); + } ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq); ch_inf.bw = BRCMU_CHAN_BW_20; d11inf->encchspec(&ch_inf); @@ -486,7 +737,7 @@ send_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key) return err; } -static void +void brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev) { struct brcmf_cfg80211_vif *vif; @@ -520,40 +771,227 @@ static int brcmf_get_first_free_bsscfgidx(struct brcmf_pub *drvr) return -ENOMEM; } +static void brcmf_set_vif_sta_macaddr(struct brcmf_if *ifp, u8 *mac_addr) +{ + u8 mac_idx = ifp->drvr->sta_mac_idx; + + /* set difference MAC address with locally administered bit */ + memcpy(mac_addr, ifp->mac_addr, ETH_ALEN); + mac_addr[0] |= 0x02; + mac_addr[3] ^= mac_idx ? 0xC0 : 0xA0; + mac_idx++; + mac_idx = mac_idx % 2; + ifp->drvr->sta_mac_idx = mac_idx; +} + +static int brcmf_cfg80211_request_sta_if(struct brcmf_if *ifp, u8 *macaddr) +{ + struct wl_interface_create_v1 iface_v1; + struct wl_interface_create_v2 iface_v2; + struct wl_interface_create_v3 iface_v3; + u32 iface_create_ver; + int err; + + /* interface_create version 1 */ + memset(&iface_v1, 0, sizeof(iface_v1)); + iface_v1.ver = WL_INTERFACE_CREATE_VER_1; + iface_v1.flags = WL_INTERFACE_CREATE_STA | + WL_INTERFACE_MAC_USE; + if (!is_zero_ether_addr(macaddr)) + memcpy(iface_v1.mac_addr, macaddr, ETH_ALEN); + else + brcmf_set_vif_sta_macaddr(ifp, iface_v1.mac_addr); + + err = brcmf_fil_iovar_data_get(ifp, "interface_create", + &iface_v1, + sizeof(iface_v1)); + if (err) { + brcmf_dbg(INFO, "failed to create interface(v1), err=%d\n", + err); + } else { + brcmf_dbg(INFO, "interface created(v1)\n"); + return 0; + } + + /* interface_create version 2 */ + memset(&iface_v2, 0, sizeof(iface_v2)); + iface_v2.ver = WL_INTERFACE_CREATE_VER_2; + iface_v2.flags = WL_INTERFACE_MAC_USE; + iface_v2.iftype = WL_INTERFACE_CREATE_STA; + if (!is_zero_ether_addr(macaddr)) + memcpy(iface_v2.mac_addr, macaddr, ETH_ALEN); + else + brcmf_set_vif_sta_macaddr(ifp, iface_v2.mac_addr); + + err = brcmf_fil_iovar_data_get(ifp, "interface_create", + &iface_v2, + sizeof(iface_v2)); + if (err) { + brcmf_dbg(INFO, "failed to create interface(v2), err=%d\n", + err); + } else { + brcmf_dbg(INFO, "interface created(v2)\n"); + return 0; + } + + /* interface_create version 3+ */ + /* get supported version from firmware side */ + iface_create_ver = 0; + err = brcmf_fil_bsscfg_int_get(ifp, "interface_create", + &iface_create_ver); + if (err) { + brcmf_err("fail to get supported version, err=%d\n", err); + return -EOPNOTSUPP; + } + + switch (iface_create_ver) { + case WL_INTERFACE_CREATE_VER_3: + memset(&iface_v3, 0, sizeof(iface_v3)); + iface_v3.ver = WL_INTERFACE_CREATE_VER_3; + iface_v3.flags = WL_INTERFACE_MAC_USE; + iface_v3.iftype = WL_INTERFACE_CREATE_STA; + if (!is_zero_ether_addr(macaddr)) + memcpy(iface_v3.mac_addr, macaddr, ETH_ALEN); + else + brcmf_set_vif_sta_macaddr(ifp, iface_v3.mac_addr); + + err = brcmf_fil_iovar_data_get(ifp, "interface_create", + &iface_v3, + sizeof(iface_v3)); + + if (!err) + brcmf_dbg(INFO, "interface created(v3)\n"); + break; + default: + brcmf_err("not support interface create(v%d)\n", + iface_create_ver); + err = -EOPNOTSUPP; + break; + } + + if (err) { + brcmf_info("station interface creation failed (%d)\n", + err); + return -EIO; + } + + return 0; +} + static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp) { + struct wl_interface_create_v1 iface_v1; + struct wl_interface_create_v2 iface_v2; + struct wl_interface_create_v3 iface_v3; + u32 iface_create_ver; struct brcmf_pub *drvr = ifp->drvr; struct brcmf_mbss_ssid_le mbss_ssid_le; int bsscfgidx; int err; - memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le)); - bsscfgidx = brcmf_get_first_free_bsscfgidx(ifp->drvr); - if (bsscfgidx < 0) - return bsscfgidx; + /* interface_create version 1 */ + memset(&iface_v1, 0, sizeof(iface_v1)); + iface_v1.ver = WL_INTERFACE_CREATE_VER_1; + iface_v1.flags = WL_INTERFACE_CREATE_AP | + WL_INTERFACE_MAC_USE; - mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx); - mbss_ssid_le.SSID_len = cpu_to_le32(5); - sprintf(mbss_ssid_le.SSID, "ssid%d" , bsscfgidx); + brcmf_set_vif_sta_macaddr(ifp, iface_v1.mac_addr); - err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le, - sizeof(mbss_ssid_le)); - if (err < 0) - bphy_err(drvr, "setting ssid failed %d\n", err); + err = brcmf_fil_iovar_data_get(ifp, "interface_create", + &iface_v1, + sizeof(iface_v1)); + if (err) { + brcmf_dbg(INFO, "failed to create interface(v1), err=%d\n", + err); + } else { + brcmf_dbg(INFO, "interface created(v1)\n"); + return 0; + } + + /* interface_create version 2 */ + memset(&iface_v2, 0, sizeof(iface_v2)); + iface_v2.ver = WL_INTERFACE_CREATE_VER_2; + iface_v2.flags = WL_INTERFACE_MAC_USE; + iface_v2.iftype = WL_INTERFACE_CREATE_AP; + + brcmf_set_vif_sta_macaddr(ifp, iface_v2.mac_addr); + + err = brcmf_fil_iovar_data_get(ifp, "interface_create", + &iface_v2, + sizeof(iface_v2)); + if (err) { + brcmf_dbg(INFO, "failed to create interface(v2), err=%d\n", + err); + } else { + brcmf_dbg(INFO, "interface created(v2)\n"); + return 0; + } + + /* interface_create version 3+ */ + /* get supported version from firmware side */ + iface_create_ver = 0; + err = brcmf_fil_bsscfg_int_get(ifp, "interface_create", + &iface_create_ver); + if (err) { + brcmf_err("fail to get supported version, err=%d\n", err); + return -EOPNOTSUPP; + } + + switch (iface_create_ver) { + case WL_INTERFACE_CREATE_VER_3: + memset(&iface_v3, 0, sizeof(iface_v3)); + iface_v3.ver = WL_INTERFACE_CREATE_VER_3; + iface_v3.flags = WL_INTERFACE_MAC_USE; + iface_v3.iftype = WL_INTERFACE_CREATE_AP; + brcmf_set_vif_sta_macaddr(ifp, iface_v3.mac_addr); + + err = brcmf_fil_iovar_data_get(ifp, "interface_create", + &iface_v3, + sizeof(iface_v3)); + + if (!err) + brcmf_dbg(INFO, "interface created(v3)\n"); + break; + default: + brcmf_err("not support interface create(v%d)\n", + iface_create_ver); + err = -EOPNOTSUPP; + break; + } + + if (err) { + brcmf_info("Does not support interface_create (%d)\n", + err); + memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le)); + bsscfgidx = brcmf_get_first_free_bsscfgidx(ifp->drvr); + if (bsscfgidx < 0) + return bsscfgidx; + + mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx); + mbss_ssid_le.SSID_len = cpu_to_le32(5); + sprintf(mbss_ssid_le.SSID, "ssid%d", bsscfgidx); + + err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le, + sizeof(mbss_ssid_le)); + + if (err < 0) + bphy_err(drvr, "setting ssid failed %d\n", err); + } return err; } /** - * brcmf_ap_add_vif() - create a new AP virtual interface for multiple BSS + * brcmf_ap_add_vif() - create a new AP or STA virtual interface * * @wiphy: wiphy device of new interface. * @name: name of the new interface. - * @params: contains mac address for AP device. + * @params: contains mac address for AP or STA device. */ static -struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name, - struct vif_params *params) +struct wireless_dev *brcmf_apsta_add_vif(struct wiphy *wiphy, const char *name, + struct vif_params *params, + enum nl80211_iftype type) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); @@ -561,18 +999,24 @@ struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name, struct brcmf_cfg80211_vif *vif; int err; + if (type != NL80211_IFTYPE_STATION && type != NL80211_IFTYPE_AP) + return ERR_PTR(-EINVAL); + if (brcmf_cfg80211_vif_event_armed(cfg)) return ERR_PTR(-EBUSY); brcmf_dbg(INFO, "Adding vif \"%s\"\n", name); - vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_AP); + vif = brcmf_alloc_vif(cfg, type); if (IS_ERR(vif)) return (struct wireless_dev *)vif; brcmf_cfg80211_arm_vif_event(cfg, vif); - err = brcmf_cfg80211_request_ap_if(ifp); + if (type == NL80211_IFTYPE_STATION) + err = brcmf_cfg80211_request_sta_if(ifp, params->macaddr); + else + err = brcmf_cfg80211_request_ap_if(ifp); if (err) { brcmf_cfg80211_arm_vif_event(cfg, NULL); goto fail; @@ -719,15 +1163,15 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, } switch (type) { case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_MESH_POINT: return ERR_PTR(-EOPNOTSUPP); case NL80211_IFTYPE_MONITOR: return brcmf_mon_add_vif(wiphy, name); + case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_AP: - wdev = brcmf_ap_add_vif(wiphy, name, params); + wdev = brcmf_apsta_add_vif(wiphy, name, params, type); break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: @@ -758,9 +1202,16 @@ void brcmf_set_mpc(struct brcmf_if *ifp, int mpc) { struct brcmf_pub *drvr = ifp->drvr; s32 err = 0; + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + ifp->drvr->req_mpc = mpc; if (check_vif_up(ifp->vif)) { - err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc); + if (cfg->pwr_save) + err = brcmf_fil_iovar_int_set(ifp, "mpc", + ifp->drvr->req_mpc); + else + err = brcmf_fil_iovar_int_set(ifp, "mpc", 0); + if (err) { bphy_err(drvr, "fail to set mpc\n"); return; @@ -769,6 +1220,21 @@ void brcmf_set_mpc(struct brcmf_if *ifp, int mpc) } } +bool brcmf_is_apmode_operating(struct wiphy *wiphy) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_cfg80211_vif *vif; + bool ret = false; + + list_for_each_entry(vif, &cfg->vif_list, list) { + if (brcmf_is_apmode(vif) && + test_bit(BRCMF_VIF_STATUS_AP_CREATED, &vif->sme_state)) + ret = true; + } + + return ret; +} + s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp, bool aborted, bool fw_abort) @@ -847,8 +1313,8 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, return err; } -static int brcmf_cfg80211_del_ap_iface(struct wiphy *wiphy, - struct wireless_dev *wdev) +static int brcmf_cfg80211_del_apsta_iface(struct wiphy *wiphy, + struct wireless_dev *wdev) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct net_device *ndev = wdev->netdev; @@ -905,15 +1371,15 @@ int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev) switch (wdev->iftype) { case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_MESH_POINT: return -EOPNOTSUPP; case NL80211_IFTYPE_MONITOR: return brcmf_mon_del_vif(wiphy, wdev); + case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_AP: - return brcmf_cfg80211_del_ap_iface(wiphy, wdev); + return brcmf_cfg80211_del_apsta_iface(wiphy, wdev); case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_P2P_DEVICE: @@ -1018,6 +1484,7 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, ndev->ieee80211_ptr->iftype = type; brcmf_cfg80211_update_proto_addr_mode(&vif->wdev); + brcmf_setup_wiphybands(cfg); done: brcmf_dbg(TRACE, "Exit\n"); @@ -1211,11 +1678,6 @@ brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) if (err) goto scan_out; - err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBREQ_FLAG, - request->ie, request->ie_len); - if (err) - goto scan_out; - err = brcmf_do_escan(vif->ifp, request); if (err) goto scan_out; @@ -1416,6 +1878,8 @@ static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason, locally_generated, GFP_KERNEL); } clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state); + clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state); + clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state); clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status); brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0); if (vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_NONE) { @@ -1610,14 +2074,18 @@ static s32 brcmf_set_wpa_version(struct net_device *ndev, s32 val = 0; s32 err = 0; - if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) + if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) { val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED; - else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) - val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED; - else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_3) + } else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) { + if (sme->crypto.akm_suites[0] == WLAN_AKM_SUITE_SAE) + val = WPA3_AUTH_SAE_PSK; + else + val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED; + } else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_3) { val = WPA3_AUTH_SAE_PSK; - else + } else { val = WPA_AUTH_DISABLED; + } brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val); err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", val); if (err) { @@ -1680,6 +2148,8 @@ brcmf_set_wsec_mode(struct net_device *ndev, s32 gval = 0; s32 wsec; s32 err = 0; + u32 algos = 0, mask = 0; + if (sme->crypto.n_ciphers_pairwise) { switch (sme->crypto.ciphers_pairwise[0]) { @@ -1696,6 +2166,15 @@ brcmf_set_wsec_mode(struct net_device *ndev, case WLAN_CIPHER_SUITE_AES_CMAC: pval = AES_ENABLED; break; + case WLAN_CIPHER_SUITE_GCMP_256: + if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_GCMP)) { + brcmf_err("the low layer not support GCMP\n"); + return -EOPNOTSUPP; + } + pval = AES_ENABLED; + algos = KEY_ALGO_MASK(CRYPTO_ALGO_AES_GCM256); + mask = algos | KEY_ALGO_MASK(CRYPTO_ALGO_AES_CCM); + break; default: bphy_err(drvr, "invalid cipher pairwise (%d)\n", sme->crypto.ciphers_pairwise[0]); @@ -1717,6 +2196,15 @@ brcmf_set_wsec_mode(struct net_device *ndev, case WLAN_CIPHER_SUITE_AES_CMAC: gval = AES_ENABLED; break; + case WLAN_CIPHER_SUITE_GCMP_256: + if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_GCMP)) { + brcmf_err("the low layer not support GCMP\n"); + return -EOPNOTSUPP; + } + gval = AES_ENABLED; + algos = KEY_ALGO_MASK(CRYPTO_ALGO_AES_GCM256); + mask = algos | KEY_ALGO_MASK(CRYPTO_ALGO_AES_CCM); + break; default: bphy_err(drvr, "invalid cipher group (%d)\n", sme->crypto.cipher_group); @@ -1725,6 +2213,7 @@ brcmf_set_wsec_mode(struct net_device *ndev, } brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval); + brcmf_dbg(CONN, "algos (0x%x) mask (0x%x)\n", algos, mask); /* In case of privacy, but no security and WPS then simulate */ /* setting AES. WPS-2.0 allows no security */ if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval && @@ -1738,6 +2227,17 @@ brcmf_set_wsec_mode(struct net_device *ndev, return err; } + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_GCMP)) { + brcmf_dbg(CONN, + "set_wsdec_info algos (0x%x) mask (0x%x)\n", + algos, mask); + err = wl_set_wsec_info_algos(ifp, algos, mask); + if (err) { + brcmf_err("set wsec_info error (%d)\n", err); + return err; + } + } + sec = &profile->sec; sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0]; sec->cipher_group = sme->crypto.cipher_group; @@ -1753,6 +2253,7 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) struct brcmf_pub *drvr = ifp->drvr; s32 val; s32 err; + s32 okc_enable; const struct brcmf_tlv *rsn_ie; const u8 *ie; u32 ie_len; @@ -1760,9 +2261,12 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) u16 rsn_cap; u32 mfp; u16 count; + u16 pmkid_count; + const u8 *group_mgmt_cs = NULL; profile->use_fwsup = BRCMF_PROFILE_FWSUP_NONE; profile->is_ft = false; + profile->is_okc = false; if (!sme->crypto.n_akm_suites) return 0; @@ -1778,6 +2282,8 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) val = WPA_AUTH_UNSPECIFIED; if (sme->want_1x) profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X; + else + profile->use_fwsup = BRCMF_PROFILE_FWSUP_ROAM; break; case WLAN_AKM_SUITE_PSK: val = WPA_AUTH_PSK; @@ -1793,11 +2299,15 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) val = WPA2_AUTH_UNSPECIFIED; if (sme->want_1x) profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X; + else + profile->use_fwsup = BRCMF_PROFILE_FWSUP_ROAM; break; case WLAN_AKM_SUITE_8021X_SHA256: val = WPA2_AUTH_1X_SHA256; if (sme->want_1x) profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X; + else + profile->use_fwsup = BRCMF_PROFILE_FWSUP_ROAM; break; case WLAN_AKM_SUITE_PSK_SHA256: val = WPA2_AUTH_PSK_SHA256; @@ -1810,11 +2320,24 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) profile->is_ft = true; if (sme->want_1x) profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X; + else + profile->use_fwsup = BRCMF_PROFILE_FWSUP_ROAM; break; case WLAN_AKM_SUITE_FT_PSK: val = WPA2_AUTH_PSK | WPA2_AUTH_FT; profile->is_ft = true; break; + case WLAN_AKM_SUITE_DPP: + val = WFA_AUTH_DPP; + profile->use_fwsup = BRCMF_PROFILE_FWSUP_NONE; + break; + case WLAN_AKM_SUITE_8021X_SUITE_B_192: + val = WPA3_AUTH_1X_SUITE_B_SHA384; + if (sme->want_1x) + profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X; + else + profile->use_fwsup = BRCMF_PROFILE_FWSUP_ROAM; + break; default: bphy_err(drvr, "invalid cipher group (%d)\n", sme->crypto.cipher_group); @@ -1824,6 +2347,17 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) switch (sme->crypto.akm_suites[0]) { case WLAN_AKM_SUITE_SAE: val = WPA3_AUTH_SAE_PSK; + if (sme->crypto.sae_pwd) { + brcmf_dbg(INFO, "using SAE offload\n"); + profile->use_fwsup = BRCMF_PROFILE_FWSUP_SAE; + } else if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FWSUP) && + brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE_EXT)) { + brcmf_dbg(INFO, "using EXTSAE with PSK offload\n"); + profile->use_fwsup = BRCMF_PROFILE_FWSUP_PSK; + } + break; + case WLAN_AKM_SUITE_FT_OVER_SAE: + val = WPA3_AUTH_SAE_PSK | WPA2_AUTH_FT; if (sme->crypto.sae_pwd) { brcmf_dbg(INFO, "using SAE offload\n"); profile->use_fwsup = BRCMF_PROFILE_FWSUP_SAE; @@ -1836,8 +2370,29 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) } } - if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X) + if ((profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X) || + (profile->use_fwsup == BRCMF_PROFILE_FWSUP_ROAM)) { brcmf_dbg(INFO, "using 1X offload\n"); + err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev), "okc_enable", + &okc_enable); + if (err) { + bphy_err(drvr, "get okc_enable failed (%d)\n", err); + } else { + brcmf_dbg(INFO, "get okc_enable (%d)\n", okc_enable); + profile->is_okc = okc_enable; + } + } else if (profile->use_fwsup != BRCMF_PROFILE_FWSUP_SAE && + (val == WPA3_AUTH_SAE_PSK)) { + brcmf_dbg(INFO, "not using SAE offload\n"); + err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev), "okc_enable", + &okc_enable); + if (err) { + bphy_err(drvr, "get okc_enable failed (%d)\n", err); + } else { + brcmf_dbg(INFO, "get okc_enable (%d)\n", okc_enable); + profile->is_okc = okc_enable; + } + } if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP)) goto skip_mfp_config; @@ -1873,8 +2428,22 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) mfp = BRCMF_MFP_CAPABLE; brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "mfp", mfp); + offset += RSN_CAP_LEN; + if (mfp && (ie_len - offset >= RSN_PMKID_COUNT_LEN)) { + pmkid_count = ie[offset] + (ie[offset + 1] << 8); + offset += RSN_PMKID_COUNT_LEN + (pmkid_count * WLAN_PMKID_LEN); + if (ie_len - offset >= WPA_IE_MIN_OUI_LEN) { + group_mgmt_cs = &ie[offset]; + if (memcmp(group_mgmt_cs, RSN_OUI, TLV_OUI_LEN) == 0) { + brcmf_fil_bsscfg_data_set(ifp, "bip", + (void *)group_mgmt_cs, + WPA_IE_MIN_OUI_LEN); + } + } + } + skip_mfp_config: - brcmf_dbg(CONN, "setting wpa_auth to %d\n", val); + brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val); err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val); if (err) { bphy_err(drvr, "could not set wpa_auth (%d)\n", err); @@ -2009,25 +2578,63 @@ static void brcmf_set_join_pref(struct brcmf_if *ifp, bphy_err(drvr, "Set join_pref error (%d)\n", err); } -static s32 -brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, - struct cfg80211_connect_params *sme) +static bool +wl_cfg80211_is_oce_ap(struct brcmf_if *ifp, + struct wiphy *wiphy, const u8 *bssid_hint) { - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; - struct ieee80211_channel *chan = sme->channel; struct brcmf_pub *drvr = ifp->drvr; - struct brcmf_join_params join_params; - size_t join_params_size; - const struct brcmf_tlv *rsn_ie; - const struct brcmf_vs_tlv *wpa_ie; - const void *ie; - u32 ie_len; - struct brcmf_ext_join_params_le *ext_join_params; + const struct brcmf_tlv *ie; + const struct cfg80211_bss_ies *ies; + struct cfg80211_bss *bss; + const u8 *parse = NULL; + u32 len; + + bss = cfg80211_get_bss(wiphy, NULL, bssid_hint, 0, 0, + IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY); + if (!bss) { + bphy_err(drvr, "Unable to find AP in the cache"); + return false; + } + + if (rcu_access_pointer(bss->ies)) { + ies = rcu_access_pointer(bss->ies); + parse = ies->data; + len = ies->len; + } else { + bphy_err(drvr, "ies is NULL"); + return false; + } + + while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) { + if (wl_cfgoce_is_oce_ie((const u8 *)ie, + (u8 const **)&parse, &len) == true) { + return true; + } + } + brcmf_dbg(TRACE, "OCE IE NOT found"); + return false; +} + +static s32 +brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_connect_params *sme) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; + struct ieee80211_channel *chan = sme->channel; + struct brcmf_pub *drvr = ifp->drvr; + struct brcmf_join_params join_params; + size_t join_params_size; + const struct brcmf_tlv *rsn_ie; + const struct brcmf_vs_tlv *wpa_ie; + const void *ie; + u32 ie_len; + struct brcmf_ext_join_params_le *ext_join_params; u16 chanspec; s32 err = 0; u32 ssid_len; + bool skip_hints = ifp->drvr->settings->fw_ap_select; brcmf_dbg(TRACE, "Enter\n"); if (!check_vif_up(ifp->vif)) @@ -2038,6 +2645,20 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, return -EOPNOTSUPP; } + /* override bssid_hint for oce networks */ + skip_hints = (skip_hints && + wl_cfg80211_is_oce_ap(ifp, wiphy, sme->bssid_hint)); + if (skip_hints) { + /* Let fw choose the best AP */ + brcmf_dbg(TRACE, "Skipping bssid & channel hint\n"); + } else { + if (sme->channel_hint) + chan = sme->channel_hint; + + if (sme->bssid_hint) + sme->bssid = sme->bssid_hint; + } + if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) { /* A normal (non P2P) connection request setup. */ ie = NULL; @@ -2113,44 +2734,50 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, goto done; } - if (sme->crypto.psk && - profile->use_fwsup != BRCMF_PROFILE_FWSUP_SAE) { - if (WARN_ON(profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE)) { - err = -EINVAL; - goto done; + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FWSUP)) { + if (sme->crypto.psk) { + if ((profile->use_fwsup != BRCMF_PROFILE_FWSUP_SAE) && + (profile->use_fwsup != BRCMF_PROFILE_FWSUP_PSK)) { + if (WARN_ON(profile->use_fwsup != + BRCMF_PROFILE_FWSUP_NONE)) { + err = -EINVAL; + goto done; + } + brcmf_dbg(INFO, "using PSK offload\n"); + profile->use_fwsup = BRCMF_PROFILE_FWSUP_PSK; + } } - brcmf_dbg(INFO, "using PSK offload\n"); - profile->use_fwsup = BRCMF_PROFILE_FWSUP_PSK; - } - if (profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE) { - /* enable firmware supplicant for this interface */ - err = brcmf_fil_iovar_int_set(ifp, "sup_wpa", 1); - if (err < 0) { - bphy_err(drvr, "failed to enable fw supplicant\n"); - goto done; + if (profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE) { + /* enable firmware supplicant for this interface */ + err = brcmf_fil_iovar_int_set(ifp, "sup_wpa", 1); + if (err < 0) { + bphy_err(drvr, "failed to enable fw supplicant\n"); + goto done; + } + } else { + err = brcmf_fil_iovar_int_set(ifp, "sup_wpa", 0); } - } - if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_PSK) - err = brcmf_set_pmk(ifp, sme->crypto.psk, - BRCMF_WSEC_MAX_PSK_LEN); - else if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_SAE) { - /* clean up user-space RSNE */ - err = brcmf_fil_iovar_data_set(ifp, "wpaie", NULL, 0); - if (err) { - bphy_err(drvr, "failed to clean up user-space RSNE\n"); - goto done; - } - err = brcmf_set_sae_password(ifp, sme->crypto.sae_pwd, - sme->crypto.sae_pwd_len); - if (!err && sme->crypto.psk) + if ((profile->use_fwsup == BRCMF_PROFILE_FWSUP_PSK) && + sme->crypto.psk) err = brcmf_set_pmk(ifp, sme->crypto.psk, BRCMF_WSEC_MAX_PSK_LEN); + else if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_SAE) { + /* clean up user-space RSNE */ + if (brcmf_fil_iovar_data_set(ifp, "wpaie", NULL, 0)) { + bphy_err(drvr, "failed to clean up user-space RSNE\n"); + goto done; + } + err = brcmf_set_sae_password(ifp, sme->crypto.sae_pwd, + sme->crypto.sae_pwd_len); + if (!err && sme->crypto.psk) + err = brcmf_set_pmk(ifp, sme->crypto.psk, + BRCMF_WSEC_MAX_PSK_LEN); + } + if (err) + goto done; } - if (err) - goto done; - /* Join with specific BSSID and cached SSID * If SSID is zero join based on BSSID only */ @@ -2181,17 +2808,25 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, if (cfg->channel) { ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1); - ext_join_params->assoc_le.chanspec_list[0] = cpu_to_le16(chanspec); + /* Increase dwell time to receive probe response or detect * beacon from target AP at a noisy air only during connect * command. */ - ext_join_params->scan_le.active_time = - cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS); - ext_join_params->scan_le.passive_time = - cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS); + if (BRCMU_CHSPEC_IS6G(chanspec)) { + ext_join_params->scan_le.active_time = + cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS_6E); + ext_join_params->scan_le.passive_time = + cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS_6E); + } else { + ext_join_params->scan_le.active_time = + cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS); + ext_join_params->scan_le.passive_time = + cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS); + } + /* To sync with presence period of VSDB GO send probe request * more frequently. Probe request will be stopped when it gets * probe response from target AP/GO. @@ -2207,8 +2842,15 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, brcmf_set_join_pref(ifp, &sme->bss_select); - err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params, - join_params_size); + if (sme->prev_bssid) { + brcmf_dbg(CONN, "Trying to REASSOC\n"); + join_params_size = sizeof(ext_join_params->assoc_le); + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_REASSOC, + &ext_join_params->assoc_le, join_params_size); + } else { + err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params, + join_params_size); + } kfree(ext_join_params); if (!err) /* This is it. join command worked, we are done */ @@ -2260,6 +2902,8 @@ brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev, clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state); clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); + clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &ifp->vif->sme_state); + clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &ifp->vif->sme_state); cfg80211_disconnected(ndev, reason_code, NULL, 0, true, GFP_KERNEL); memcpy(&scbval.ea, &profile->bssid, ETH_ALEN); @@ -2435,6 +3079,7 @@ brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, s32 err; u8 keybuf[8]; bool ext_key; + u32 algos = 0, mask = 0; brcmf_dbg(TRACE, "Enter\n"); brcmf_dbg(CONN, "key index (%d)\n", key_idx); @@ -2516,6 +3161,30 @@ brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, val = AES_ENABLED; brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n"); break; + case WLAN_CIPHER_SUITE_GCMP_256: + if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_GCMP)) { + brcmf_err("the low layer not support GCMP\n"); + err = -EOPNOTSUPP; + goto done; + } + key->algo = CRYPTO_ALGO_AES_GCM256; + val = AES_ENABLED; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_GCMP_256\n"); + algos = KEY_ALGO_MASK(CRYPTO_ALGO_AES_GCM256); + mask = algos | KEY_ALGO_MASK(CRYPTO_ALGO_AES_CCM); + break; + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_GCMP)) { + brcmf_err("the low layer not support GCMP\n"); + err = -EOPNOTSUPP; + goto done; + } + key->algo = CRYPTO_ALGO_BIP_GMAC256; + val = AES_ENABLED; + algos = KEY_ALGO_MASK(CRYPTO_ALGO_BIP_GMAC256); + mask = algos | KEY_ALGO_MASK(CRYPTO_ALGO_AES_CCM); + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_BIP_GMAC_256\n"); + break; default: bphy_err(drvr, "Invalid cipher (0x%x)\n", params->cipher); err = -EINVAL; @@ -2538,6 +3207,17 @@ brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, goto done; } + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_GCMP)) { + brcmf_dbg(CONN, + "set_wsdec_info algos (0x%x) mask (0x%x)\n", + algos, mask); + err = wl_set_wsec_info_algos(ifp, algos, mask); + if (err) { + brcmf_err("set wsec_info error (%d)\n", err); + return err; + } + } + done: brcmf_dbg(TRACE, "Exit\n"); return err; @@ -2931,12 +3611,15 @@ brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev, goto done; } - pm = enabled ? PM_FAST : PM_OFF; + pm = enabled ? ifp->drvr->settings->default_pm : PM_OFF; /* Do not enable the power save after assoc if it is a p2p interface */ if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) { brcmf_dbg(INFO, "Do not enable power save for P2P clients\n"); pm = PM_OFF; } + + brcmf_set_mpc(ifp, ifp->drvr->req_mpc); + brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled")); err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm); @@ -2947,11 +3630,6 @@ brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev, bphy_err(drvr, "error (%d)\n", err); } - err = brcmf_fil_iovar_int_set(ifp, "pm2_sleep_ret", - min_t(u32, timeout, BRCMF_PS_MAX_TIMEOUT_MS)); - if (err) - bphy_err(drvr, "Unable to set pm timeout, (%d)\n", err); - done: brcmf_dbg(TRACE, "Exit\n"); return err; @@ -2972,24 +3650,20 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg, u8 *notify_ie; size_t notify_ielen; struct cfg80211_inform_bss bss_data = {}; + const struct brcmf_tlv *ssid = NULL; if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) { bphy_err(drvr, "Bss info is larger than buffer. Discarding\n"); return -EINVAL; } - if (!bi->ctl_ch) { - ch.chspec = le16_to_cpu(bi->chanspec); - cfg->d11inf.decchspec(&ch); + ch.chspec = le16_to_cpu(bi->chanspec); + cfg->d11inf.decchspec(&ch); + if (!bi->ctl_ch) bi->ctl_ch = ch.control_ch_num; - } - channel = bi->ctl_ch; - - if (channel <= CH_MAX_2G_CHANNEL) - band = NL80211_BAND_2GHZ; - else - band = NL80211_BAND_5GHZ; + channel = bi->ctl_ch; + band = BRCMU_CHAN_BAND_TO_NL80211(ch.band); freq = ieee80211_channel_to_frequency(channel, band); bss_data.chan = ieee80211_get_channel(wiphy, freq); bss_data.scan_width = NL80211_BSS_CHAN_WIDTH_20; @@ -3001,6 +3675,12 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg, notify_ielen = le32_to_cpu(bi->ie_length); bss_data.signal = (s16)le16_to_cpu(bi->RSSI) * 100; + ssid = brcmf_parse_tlvs(notify_ie, notify_ielen, WLAN_EID_SSID); + if (ssid && ssid->data[0] == '\0' && ssid->len == bi->SSID_len) { + /* Update SSID for hidden AP */ + memcpy((u8 *)ssid->data, bi->SSID, bi->SSID_len); + } + brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID); brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq); brcmf_dbg(CONN, "Capability: %X\n", notify_capability); @@ -3097,11 +3777,7 @@ static s32 brcmf_inform_ibss(struct brcmf_cfg80211_info *cfg, ch.chspec = le16_to_cpu(bi->chanspec); cfg->d11inf.decchspec(&ch); - if (ch.band == BRCMU_CHAN_BAND_2G) - band = wiphy->bands[NL80211_BAND_2GHZ]; - else - band = wiphy->bands[NL80211_BAND_5GHZ]; - + band = wiphy->bands[BRCMU_CHAN_BAND_TO_NL80211(ch.band)]; freq = ieee80211_channel_to_frequency(ch.control_ch_num, band->band); cfg->channel = freq; notify_channel = ieee80211_get_channel(wiphy, freq); @@ -3143,10 +3819,7 @@ static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp) { struct brcmf_pub *drvr = cfg->pub; - struct brcmf_bss_info_le *bi; - const struct brcmf_tlv *tim; - size_t ie_len; - u8 *ie; + struct brcmf_bss_info_le *bi = NULL; s32 err = 0; brcmf_dbg(TRACE, "Enter\n"); @@ -3160,29 +3833,8 @@ static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg, bphy_err(drvr, "Could not get bss info %d\n", err); goto update_bss_info_out; } - bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4); err = brcmf_inform_single_bss(cfg, bi); - if (err) - goto update_bss_info_out; - - ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset); - ie_len = le32_to_cpu(bi->ie_length); - - tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM); - if (!tim) { - /* - * active scan was done so we could not get dtim - * information out of probe response. - * so we speficially query dtim information to dongle. - */ - u32 var; - err = brcmf_fil_iovar_int_get(ifp, "dtim_assoc", &var); - if (err) { - bphy_err(drvr, "wl dtim_assoc failed (%d)\n", err); - goto update_bss_info_out; - } - } update_bss_info_out: brcmf_dbg(TRACE, "Exit"); @@ -3806,17 +4458,34 @@ static s32 brcmf_cfg80211_resume(struct wiphy *wiphy) struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct net_device *ndev = cfg_to_ndev(cfg); struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_pub *drvr = ifp->drvr; + struct brcmf_bus *bus_if = drvr->bus_if; + struct brcmf_cfg80211_info *config = drvr->config; + int retry = BRCMF_PM_WAIT_MAXRETRY; + s32 power_mode; + + power_mode = cfg->pwr_save ? ifp->drvr->settings->default_pm : PM_OFF; brcmf_dbg(TRACE, "Enter\n"); + config->pm_state = BRCMF_CFG80211_PM_STATE_RESUMING; + if (cfg->wowl.active) { + /* wait for bus resumed */ + while (retry && bus_if->state != BRCMF_BUS_UP) { + usleep_range(10000, 20000); + retry--; + } + if (!retry && bus_if->state != BRCMF_BUS_UP) + brcmf_err("timed out wait for bus resume\n"); + brcmf_report_wowl_wakeind(wiphy, ifp); brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0); brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0); if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND)) brcmf_configure_arp_nd_offload(ifp, true); brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, - cfg->wowl.pre_pmmode); + power_mode); cfg->wowl.active = false; if (cfg->wowl.nd_enabled) { brcmf_cfg80211_sched_scan_stop(cfg->wiphy, ifp->ndev, 0); @@ -3825,7 +4494,12 @@ static s32 brcmf_cfg80211_resume(struct wiphy *wiphy) brcmf_notify_sched_scan_results); cfg->wowl.nd_enabled = false; } + + /* disable packet filters */ + brcmf_pktfilter_enable(ifp->ndev, false); + } + config->pm_state = BRCMF_CFG80211_PM_STATE_RESUMED; return 0; } @@ -3841,7 +4515,6 @@ static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg, if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND)) brcmf_configure_arp_nd_offload(ifp, false); - brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->wowl.pre_pmmode); brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX); wowl_config = 0; @@ -3883,6 +4556,9 @@ static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg, brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1); brcmf_bus_wowl_config(cfg->pub->bus_if, true); cfg->wowl.active = true; + + /* enable packet filters */ + brcmf_pktfilter_enable(ifp->ndev, true); } static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy, @@ -3892,9 +4568,12 @@ static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy, struct net_device *ndev = cfg_to_ndev(cfg); struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_cfg80211_vif *vif; + struct brcmf_cfg80211_info *config = ifp->drvr->config; brcmf_dbg(TRACE, "Enter\n"); + config->pm_state = BRCMF_CFG80211_PM_STATE_SUSPENDING; + /* if the primary net_device is not READY there is nothing * we can do but pray resume goes smoothly. */ @@ -3909,7 +4588,8 @@ static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy, if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) brcmf_abort_scanning(cfg); - if (wowl == NULL) { + if (!wowl || !test_bit(BRCMF_VIF_STATUS_CONNECTED, + &ifp->vif->sme_state)) { brcmf_bus_wowl_config(cfg->pub->bus_if, false); list_for_each_entry(vif, &cfg->vif_list, list) { if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) @@ -3929,14 +4609,19 @@ static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy, brcmf_set_mpc(ifp, 1); } else { - /* Configure WOWL paramaters */ - brcmf_configure_wowl(cfg, ifp, wowl); + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) + /* Configure WOWL parameters */ + brcmf_configure_wowl(cfg, ifp, wowl); } exit: - brcmf_dbg(TRACE, "Exit\n"); + /* set cfg80211 pm state to cfg80211 suspended state */ + config->pm_state = BRCMF_CFG80211_PM_STATE_SUSPENDED; + /* clear any scanning activity */ cfg->scan_status = 0; + + brcmf_dbg(TRACE, "Exit\n"); return 0; } @@ -3992,11 +4677,23 @@ brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev, return -EINVAL; } - brcmf_dbg(CONN, "set_pmksa - PMK bssid: %pM =\n", pmk[npmk].bssid); - brcmf_dbg(CONN, "%*ph\n", WLAN_PMKID_LEN, pmk[npmk].pmkid); + brcmf_dbg(CONN, "set_pmksa - PMK bssid: %pM =\n", pmk[i].bssid); + brcmf_dbg(CONN, "%*ph\n", WLAN_PMKID_LEN, pmk[i].pmkid); err = brcmf_update_pmklist(cfg, ifp); + if (pmksa->pmk_len && pmksa->pmk_len < BRCMF_WSEC_PMK_LEN_SUITEB_192) { + /* external supplicant stores SUITEB-192 PMK */ + if (ifp->vif->profile.is_okc) { + err = brcmf_fil_iovar_data_set(ifp, "okc_info_pmk", pmksa->pmk, + pmksa->pmk_len); + if (err < 0) + bphy_err(drvr, "okc_info_pmk iovar failed: ret=%d\n", err); + } else { + brcmf_set_pmk(ifp, pmksa->pmk, pmksa->pmk_len); + } + } + brcmf_dbg(TRACE, "Exit\n"); return err; } @@ -4102,6 +4799,12 @@ static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie) return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0); } +static bool brcmf_valid_dpp_suite(u8 *oui) +{ + return (memcmp(oui, WFA_OUI, TLV_OUI_LEN) == 0 && + *(oui + TLV_OUI_LEN) == DPP_AKM_SUITE_TYPE); +} + static s32 brcmf_configure_wpaie(struct brcmf_if *ifp, const struct brcmf_vs_tlv *wpa_ie, @@ -4215,42 +4918,47 @@ brcmf_configure_wpaie(struct brcmf_if *ifp, goto exit; } for (i = 0; i < count; i++) { - if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) { + if (brcmf_valid_dpp_suite(&data[offset])) { + wpa_auth |= WFA_AUTH_DPP; + offset += TLV_OUI_LEN; + } else if (brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) { + offset += TLV_OUI_LEN; + switch (data[offset]) { + case RSN_AKM_NONE: + brcmf_dbg(TRACE, "RSN_AKM_NONE\n"); + wpa_auth |= WPA_AUTH_NONE; + break; + case RSN_AKM_UNSPECIFIED: + brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n"); + is_rsn_ie ? + (wpa_auth |= WPA2_AUTH_UNSPECIFIED) : + (wpa_auth |= WPA_AUTH_UNSPECIFIED); + break; + case RSN_AKM_PSK: + brcmf_dbg(TRACE, "RSN_AKM_PSK\n"); + is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) : + (wpa_auth |= WPA_AUTH_PSK); + break; + case RSN_AKM_SHA256_PSK: + brcmf_dbg(TRACE, "RSN_AKM_MFP_PSK\n"); + wpa_auth |= WPA2_AUTH_PSK_SHA256; + break; + case RSN_AKM_SHA256_1X: + brcmf_dbg(TRACE, "RSN_AKM_MFP_1X\n"); + wpa_auth |= WPA2_AUTH_1X_SHA256; + break; + case RSN_AKM_SAE: + brcmf_dbg(TRACE, "RSN_AKM_SAE\n"); + wpa_auth |= WPA3_AUTH_SAE_PSK; + break; + default: + bphy_err(drvr, "Invalid key mgmt info\n"); + } + } else { err = -EINVAL; bphy_err(drvr, "ivalid OUI\n"); goto exit; } - offset += TLV_OUI_LEN; - switch (data[offset]) { - case RSN_AKM_NONE: - brcmf_dbg(TRACE, "RSN_AKM_NONE\n"); - wpa_auth |= WPA_AUTH_NONE; - break; - case RSN_AKM_UNSPECIFIED: - brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n"); - is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) : - (wpa_auth |= WPA_AUTH_UNSPECIFIED); - break; - case RSN_AKM_PSK: - brcmf_dbg(TRACE, "RSN_AKM_PSK\n"); - is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) : - (wpa_auth |= WPA_AUTH_PSK); - break; - case RSN_AKM_SHA256_PSK: - brcmf_dbg(TRACE, "RSN_AKM_MFP_PSK\n"); - wpa_auth |= WPA2_AUTH_PSK_SHA256; - break; - case RSN_AKM_SHA256_1X: - brcmf_dbg(TRACE, "RSN_AKM_MFP_1X\n"); - wpa_auth |= WPA2_AUTH_1X_SHA256; - break; - case RSN_AKM_SAE: - brcmf_dbg(TRACE, "RSN_AKM_SAE\n"); - wpa_auth |= WPA3_AUTH_SAE_PSK; - break; - default: - bphy_err(drvr, "Invalid key mgmt info\n"); - } offset++; } @@ -4270,10 +4978,12 @@ brcmf_configure_wpaie(struct brcmf_if *ifp, */ if (!(wpa_auth & (WPA2_AUTH_PSK_SHA256 | WPA2_AUTH_1X_SHA256 | + WFA_AUTH_DPP | WPA3_AUTH_SAE_PSK))) { err = -EINVAL; goto exit; } + /* Firmware has requirement that WPA2_AUTH_PSK/ * WPA2_AUTH_UNSPECIFIED be set, if SHA256 OUI * is to be included in the rsn ie. @@ -4709,6 +5419,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, settings->inactivity_timeout); dev_role = ifp->vif->wdev.iftype; mbss = ifp->vif->mbss; + brcmf_dbg(TRACE, "mbss %s\n", mbss ? "enabled" : "disabled"); /* store current 11d setting */ if (brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY, @@ -4798,7 +5509,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, err = -EINVAL; goto exit; } - + ifp->isap = false; /* Interface specific setup */ if (dev_role == NL80211_IFTYPE_AP) { if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss)) @@ -4810,17 +5521,17 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, err); goto exit; } - if (!mbss) { - /* Firmware 10.x requires setting channel after enabling - * AP and before bringing interface up. - */ - err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec); - if (err < 0) { - bphy_err(drvr, "Set Channel failed: chspec=%d, %d\n", - chanspec, err); - goto exit; - } + + /* Firmware 10.x requires setting channel after enabling + * AP and before bringing interface up. + */ + err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec); + if (err < 0) { + bphy_err(drvr, "Set Channel failed: chspec=%d, %d\n", + chanspec, err); + goto exit; } + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1); if (err < 0) { bphy_err(drvr, "BRCMF_C_UP error (%d)\n", err); @@ -4878,7 +5589,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, err); goto exit; } - + ifp->isap = true; brcmf_dbg(TRACE, "AP mode configuration complete\n"); } else if (dev_role == NL80211_IFTYPE_P2P_GO) { err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec); @@ -4910,6 +5621,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, goto exit; } + ifp->isap = true; brcmf_dbg(TRACE, "GO mode configuration complete\n"); } else { WARN_ON(1); @@ -4923,6 +5635,9 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, if ((err) && (!mbss)) { brcmf_set_mpc(ifp, 1); brcmf_configure_arp_nd_offload(ifp, true); + } else { + cfg->num_softap++; + brcmf_dbg(TRACE, "Num of SoftAP %u\n", cfg->num_softap); } return err; } @@ -4936,6 +5651,7 @@ static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev) s32 err; struct brcmf_fil_bss_enable_le bss_enable; struct brcmf_join_params join_params; + s32 apsta = 0; brcmf_dbg(TRACE, "Enter\n"); @@ -4952,26 +5668,43 @@ static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev) profile->use_fwauth = BIT(BRCMF_PROFILE_FWAUTH_NONE); } - if (ifp->vif->mbss) { - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); - return err; - } + cfg->num_softap--; - /* First BSS doesn't get a full reset */ - if (ifp->bsscfgidx == 0) - brcmf_fil_iovar_int_set(ifp, "closednet", 0); + /* Clear bss configuration and SSID */ + bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx); + bss_enable.enable = cpu_to_le32(0); + err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable, + sizeof(bss_enable)); + if (err < 0) + brcmf_err("bss_enable config failed %d\n", err); memset(&join_params, 0, sizeof(join_params)); err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID, &join_params, sizeof(join_params)); if (err < 0) bphy_err(drvr, "SET SSID error (%d)\n", err); - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); - if (err < 0) - bphy_err(drvr, "BRCMF_C_DOWN error %d\n", err); - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0); + + if (cfg->num_softap) { + brcmf_dbg(TRACE, "Num of SoftAP %u\n", cfg->num_softap); + return 0; + } + + /* First BSS doesn't get a full reset */ + if (ifp->bsscfgidx == 0) + brcmf_fil_iovar_int_set(ifp, "closednet", 0); + + err = brcmf_fil_iovar_int_get(ifp, "apsta", &apsta); if (err < 0) - bphy_err(drvr, "setting AP mode failed %d\n", err); + brcmf_err("wl apsta failed (%d)\n", err); + + if (!apsta) { + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); + if (err < 0) + bphy_err(drvr, "BRCMF_C_DOWN error %d\n", err); + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0); + if (err < 0) + bphy_err(drvr, "Set AP mode error %d\n", err); + } if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) brcmf_fil_iovar_int_set(ifp, "mbss", 0); brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY, @@ -4991,8 +5724,8 @@ static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev) bphy_err(drvr, "bss_enable config failed %d\n", err); } brcmf_set_mpc(ifp, 1); - brcmf_configure_arp_nd_offload(ifp, true); clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state); + brcmf_configure_arp_nd_offload(ifp, true); brcmf_net_setcarrier(ifp, false); return err; @@ -5104,9 +5837,13 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, s32 ie_len; struct brcmf_fil_action_frame_le *action_frame; struct brcmf_fil_af_params_le *af_params; - bool ack; + bool ack = false; s32 chan_nr; u32 freq; + struct brcmf_mf_params_le *mf_params; + u32 mf_params_len; + s32 timeout; + u32 hw_channel; brcmf_dbg(TRACE, "Enter\n"); @@ -5167,13 +5904,16 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, /* Add the channel. Use the one specified as parameter if any or * the current one (got from the firmware) otherwise */ - if (chan) + if (chan) { freq = chan->center_freq; - else + chan_nr = ieee80211_frequency_to_channel(freq); + af_params->channel = cpu_to_le32(chan_nr); + } else { brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL, - &freq); - chan_nr = ieee80211_frequency_to_channel(freq); - af_params->channel = cpu_to_le32(chan_nr); + &hw_channel); + af_params->channel = hw_channel; + } + af_params->dwell_time = cpu_to_le32(params->wait); memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN], le16_to_cpu(action_frame->len)); @@ -5182,11 +5922,79 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, *cookie, le16_to_cpu(action_frame->len), freq); ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg), - af_params); + af_params, vif, chan); cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack, GFP_KERNEL); kfree(af_params); + } else if (ieee80211_is_auth(mgmt->frame_control)) { + reinit_completion(&vif->mgmt_tx); + clear_bit(BRCMF_MGMT_TX_ACK, &vif->mgmt_tx_status); + clear_bit(BRCMF_MGMT_TX_NOACK, &vif->mgmt_tx_status); + clear_bit(BRCMF_MGMT_TX_OFF_CHAN_COMPLETED, + &vif->mgmt_tx_status); + + mf_params_len = offsetof(struct brcmf_mf_params_le, data) + + (len - DOT11_MGMT_HDR_LEN); + mf_params = kzalloc(mf_params_len, GFP_KERNEL); + if (!mf_params) { + err = -ENOMEM; + goto exit; + } + + mf_params->dwell_time = cpu_to_le32(MGMT_AUTH_FRAME_DWELL_TIME); + mf_params->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN); + mf_params->frame_control = mgmt->frame_control; + + if (chan) { + freq = chan->center_freq; + chan_nr = ieee80211_frequency_to_channel(freq); + mf_params->channel = cpu_to_le32(chan_nr); + } else { + brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL, + &hw_channel); + mf_params->channel = hw_channel; + } + + memcpy(&mf_params->da[0], &mgmt->da[0], ETH_ALEN); + memcpy(&mf_params->bssid[0], &mgmt->bssid[0], ETH_ALEN); + mf_params->packet_id = cpu_to_le32(*cookie); + memcpy(mf_params->data, &buf[DOT11_MGMT_HDR_LEN], + le16_to_cpu(mf_params->len)); + + brcmf_dbg(TRACE, "Auth frame, cookie=%d, fc=%04x, len=%d, channel=%d\n", + le32_to_cpu(mf_params->packet_id), + le16_to_cpu(mf_params->frame_control), + le16_to_cpu(mf_params->len), + le32_to_cpu(mf_params->channel)); + + vif->mgmt_tx_id = le32_to_cpu(mf_params->packet_id); + set_bit(BRCMF_MGMT_TX_SEND_FRAME, &vif->mgmt_tx_status); + + err = brcmf_fil_bsscfg_data_set(vif->ifp, "mgmt_frame", + mf_params, mf_params_len); + if (err) { + bphy_err(drvr, "Failed to send Auth frame: err=%d\n", + err); + goto tx_status; + } + + timeout = + wait_for_completion_timeout(&vif->mgmt_tx, + MGMT_AUTH_FRAME_WAIT_TIME); + if (test_bit(BRCMF_MGMT_TX_ACK, &vif->mgmt_tx_status)) { + brcmf_dbg(TRACE, "TX Auth frame operation is success\n"); + ack = true; + } else { + bphy_err(drvr, "TX Auth frame operation is failed: status=%ld)\n", + vif->mgmt_tx_status); + } + +tx_status: + cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack, + GFP_KERNEL); + kfree(mf_params); + } else { brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control); brcmf_dbg_hex_dump(true, buf, len, "payload, len=%zu\n", len); @@ -5244,15 +6052,7 @@ static int brcmf_cfg80211_get_channel(struct wiphy *wiphy, ch.chspec = chanspec; cfg->d11inf.decchspec(&ch); - - switch (ch.band) { - case BRCMU_CHAN_BAND_2G: - band = NL80211_BAND_2GHZ; - break; - case BRCMU_CHAN_BAND_5G: - band = NL80211_BAND_5GHZ; - break; - } + band = BRCMU_CHAN_BAND_TO_NL80211(ch.band); switch (ch.bw) { case BRCMU_CHAN_BW_80: @@ -5440,17 +6240,30 @@ static int brcmf_cfg80211_set_pmk(struct wiphy *wiphy, struct net_device *dev, const struct cfg80211_pmk_conf *conf) { struct brcmf_if *ifp; + struct brcmf_pub *drvr; + int ret; brcmf_dbg(TRACE, "enter\n"); /* expect using firmware supplicant for 1X */ ifp = netdev_priv(dev); - if (WARN_ON(ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_1X)) + drvr = ifp->drvr; + if (WARN_ON((ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_1X) && + (ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_ROAM) && + (ifp->vif->profile.is_ft != true) && + (ifp->vif->profile.is_okc != true))) return -EINVAL; if (conf->pmk_len > BRCMF_WSEC_MAX_PSK_LEN) return -ERANGE; + if (ifp->vif->profile.is_okc) { + ret = brcmf_fil_iovar_data_set(ifp, "okc_info_pmk", conf->pmk, + conf->pmk_len); + if (ret < 0) + bphy_err(drvr, "okc_info_pmk iovar failed: ret=%d\n", ret); + } + return brcmf_set_pmk(ifp, conf->pmk, conf->pmk_len); } @@ -5467,26 +6280,127 @@ static int brcmf_cfg80211_del_pmk(struct wiphy *wiphy, struct net_device *dev, return brcmf_set_pmk(ifp, NULL, 0); } -static struct cfg80211_ops brcmf_cfg80211_ops = { - .add_virtual_intf = brcmf_cfg80211_add_iface, - .del_virtual_intf = brcmf_cfg80211_del_iface, - .change_virtual_intf = brcmf_cfg80211_change_iface, - .scan = brcmf_cfg80211_scan, - .set_wiphy_params = brcmf_cfg80211_set_wiphy_params, - .join_ibss = brcmf_cfg80211_join_ibss, - .leave_ibss = brcmf_cfg80211_leave_ibss, - .get_station = brcmf_cfg80211_get_station, - .dump_station = brcmf_cfg80211_dump_station, - .set_tx_power = brcmf_cfg80211_set_tx_power, - .get_tx_power = brcmf_cfg80211_get_tx_power, - .add_key = brcmf_cfg80211_add_key, - .del_key = brcmf_cfg80211_del_key, - .get_key = brcmf_cfg80211_get_key, - .set_default_key = brcmf_cfg80211_config_default_key, - .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key, - .set_power_mgmt = brcmf_cfg80211_set_power_mgmt, - .connect = brcmf_cfg80211_connect, - .disconnect = brcmf_cfg80211_disconnect, +static int +brcmf_cfg80211_change_bss(struct wiphy *wiphy, struct net_device *dev, + struct bss_parameters *params) +{ + struct brcmf_if *ifp; + int ret = 0; + u32 ap_isolate, val; + + brcmf_dbg(TRACE, "Enter\n"); + ifp = netdev_priv(dev); + if (params->ap_isolate >= 0) { + ap_isolate = (u32)params->ap_isolate; + ret = brcmf_fil_iovar_int_set(ifp, "ap_isolate", ap_isolate); + if (ret < 0) + brcmf_err("ap_isolate iovar failed: ret=%d\n", ret); + } + + /* Get ap_isolate value from firmware to detemine whether fmac */ + /* driver supports packet forwarding. */ + if (brcmf_fil_iovar_int_get(ifp, "ap_isolate", &val) == 0) { + ifp->fmac_pkt_fwd_en = + ((params->ap_isolate == 0) && (val == 1)) ? + true : false; + } else { + brcmf_err("get ap_isolate iovar failed: ret=%d\n", ret); + ifp->fmac_pkt_fwd_en = false; + } + + return ret; +} + +static int +brcmf_cfg80211_external_auth(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_external_auth_params *params) +{ + struct brcmf_if *ifp; + struct brcmf_pub *drvr; + struct brcmf_auth_req_status_le auth_status; + int ret = 0; + + brcmf_dbg(TRACE, "Enter\n"); + + ifp = netdev_priv(dev); + drvr = ifp->drvr; + if (params->status == WLAN_STATUS_SUCCESS) { + auth_status.flags = cpu_to_le16(BRCMF_EXTAUTH_SUCCESS); + } else { + bphy_err(drvr, "External authentication failed: status=%d\n", + params->status); + auth_status.flags = cpu_to_le16(BRCMF_EXTAUTH_FAIL); + } + + memcpy(auth_status.peer_mac, params->bssid, ETH_ALEN); + auth_status.ssid_len = cpu_to_le32(min_t(u8, params->ssid.ssid_len, + IEEE80211_MAX_SSID_LEN)); + memcpy(auth_status.ssid, params->ssid.ssid, auth_status.ssid_len); + memset(auth_status.pmkid, 0, WLAN_PMKID_LEN); + if (params->pmkid) + memcpy(auth_status.pmkid, params->pmkid, WLAN_PMKID_LEN); + + ret = brcmf_fil_iovar_data_set(ifp, "auth_status", &auth_status, + sizeof(auth_status)); + if (ret < 0) + bphy_err(drvr, "auth_status iovar failed: ret=%d\n", ret); + + return ret; +} + +static int +brcmf_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy, struct net_device *dev, + s32 rssi_thold, u32 rssi_hyst) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp; + struct wl_rssi_event rssi = {}; + int err = 0; + + ifp = netdev_priv(dev); + if (rssi_thold == 0) { + cfg->cqm_info.enable = 0; + cfg->cqm_info.rssi_threshold = 0; + } else { + cfg->cqm_info.enable = 1; + cfg->cqm_info.rssi_threshold = rssi_thold; + + rssi.rate_limit_msec = 0; + rssi.rssi_levels[rssi.num_rssi_levels++] = S8_MIN; + rssi.rssi_levels[rssi.num_rssi_levels++] = + cfg->cqm_info.rssi_threshold; + rssi.rssi_levels[rssi.num_rssi_levels++] = S8_MAX; + } + + err = brcmf_fil_iovar_data_set(ifp, "rssi_event", &rssi, sizeof(rssi)); + if (err < 0) + brcmf_err("set rssi_event iovar failed (%d)\n", err); + + brcmf_dbg(TRACE, "enable = %d, rssi_threshold = %d\n", + cfg->cqm_info.enable, cfg->cqm_info.rssi_threshold); + return err; +} + +static struct cfg80211_ops brcmf_cfg80211_ops = { + .add_virtual_intf = brcmf_cfg80211_add_iface, + .del_virtual_intf = brcmf_cfg80211_del_iface, + .change_virtual_intf = brcmf_cfg80211_change_iface, + .scan = brcmf_cfg80211_scan, + .set_wiphy_params = brcmf_cfg80211_set_wiphy_params, + .join_ibss = brcmf_cfg80211_join_ibss, + .leave_ibss = brcmf_cfg80211_leave_ibss, + .get_station = brcmf_cfg80211_get_station, + .dump_station = brcmf_cfg80211_dump_station, + .set_tx_power = brcmf_cfg80211_set_tx_power, + .get_tx_power = brcmf_cfg80211_get_tx_power, + .add_key = brcmf_cfg80211_add_key, + .del_key = brcmf_cfg80211_del_key, + .get_key = brcmf_cfg80211_get_key, + .set_default_key = brcmf_cfg80211_config_default_key, + .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key, + .set_power_mgmt = brcmf_cfg80211_set_power_mgmt, + .connect = brcmf_cfg80211_connect, + .disconnect = brcmf_cfg80211_disconnect, .suspend = brcmf_cfg80211_suspend, .resume = brcmf_cfg80211_resume, .set_pmksa = brcmf_cfg80211_set_pmksa, @@ -5513,6 +6427,9 @@ static struct cfg80211_ops brcmf_cfg80211_ops = { .update_connect_params = brcmf_cfg80211_update_conn_params, .set_pmk = brcmf_cfg80211_set_pmk, .del_pmk = brcmf_cfg80211_del_pmk, + .change_bss = brcmf_cfg80211_change_bss, + .external_auth = brcmf_cfg80211_external_auth, + .set_cqm_rssi_config = brcmf_cfg80211_set_cqm_rssi_config, }; struct cfg80211_ops *brcmf_cfg80211_get_ops(struct brcmf_mp_device *settings) @@ -5559,6 +6476,7 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, vif->mbss = mbss; } + init_completion(&vif->mgmt_tx); list_add_tail(&vif->list, &cfg->vif_list); return vif; } @@ -5577,8 +6495,10 @@ void brcmf_cfg80211_free_netdev(struct net_device *ndev) ifp = netdev_priv(ndev); vif = ifp->vif; - if (vif) + if (vif) { brcmf_free_vif(vif); + ifp->vif = NULL; + } } static bool brcmf_is_linkup(struct brcmf_cfg80211_vif *vif, @@ -5878,6 +6798,47 @@ static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg, return err; } +static bool +brcmf_has_pmkid(const u8 *parse, u32 len) +{ + const struct brcmf_tlv *rsn_ie; + const u8 *ie; + u32 ie_len; + u32 offset; + u16 count; + + rsn_ie = brcmf_parse_tlvs(parse, len, WLAN_EID_RSN); + if (!rsn_ie) + goto done; + ie = (const u8 *)rsn_ie; + ie_len = rsn_ie->len + TLV_HDR_LEN; + /* Skip group data cipher suite */ + offset = TLV_HDR_LEN + WPA_IE_VERSION_LEN + WPA_IE_MIN_OUI_LEN; + if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len) + goto done; + /* Skip pairwise cipher suite(s) */ + count = ie[offset] + (ie[offset + 1] << 8); + offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN); + if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len) + goto done; + /* Skip auth key management suite(s) */ + count = ie[offset] + (ie[offset + 1] << 8); + offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN); + if (offset + RSN_CAP_LEN >= ie_len) + goto done; + /* Skip rsn capabilities */ + offset += RSN_CAP_LEN; + if (offset + RSN_PMKID_COUNT_LEN > ie_len) + goto done; + /* Extract PMKID count */ + count = ie[offset] + (ie[offset + 1] << 8); + if (count) + return true; + +done: + return false; +} + static s32 brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg, struct net_device *ndev, @@ -5920,11 +6881,7 @@ brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg, ch.chspec = le16_to_cpu(bi->chanspec); cfg->d11inf.decchspec(&ch); - if (ch.band == BRCMU_CHAN_BAND_2G) - band = wiphy->bands[NL80211_BAND_2GHZ]; - else - band = wiphy->bands[NL80211_BAND_5GHZ]; - + band = wiphy->bands[BRCMU_CHAN_BAND_TO_NL80211(ch.band)]; freq = ieee80211_channel_to_frequency(ch.control_ch_num, band->band); notify_channel = ieee80211_get_channel(wiphy, freq); @@ -5938,14 +6895,16 @@ brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg, roam_info.resp_ie = conn_info->resp_ie; roam_info.resp_ie_len = conn_info->resp_ie_len; + if ((profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X || + profile->use_fwsup == BRCMF_PROFILE_FWSUP_ROAM) && + (brcmf_has_pmkid(roam_info.req_ie, roam_info.req_ie_len) || + profile->is_ft || profile->is_okc)) + roam_info.authorized = true; + cfg80211_roamed(ndev, &roam_info, GFP_KERNEL); brcmf_dbg(CONN, "Report roaming result\n"); - if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X && profile->is_ft) { - cfg80211_port_authorized(ndev, profile->bssid, GFP_KERNEL); - brcmf_dbg(CONN, "Report port authorized\n"); - } - + clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state); brcmf_dbg(TRACE, "Exit\n"); return err; @@ -5973,6 +6932,10 @@ brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg, &ifp->vif->sme_state); conn_params.status = WLAN_STATUS_SUCCESS; } else { + clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, + &ifp->vif->sme_state); + clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, + &ifp->vif->sme_state); conn_params.status = WLAN_STATUS_AUTH_TIMEOUT; } conn_params.bssid = profile->bssid; @@ -5980,6 +6943,11 @@ brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg, conn_params.req_ie_len = conn_info->req_ie_len; conn_params.resp_ie = conn_info->resp_ie; conn_params.resp_ie_len = conn_info->resp_ie_len; + + if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X && + brcmf_has_pmkid(conn_params.req_ie, conn_params.req_ie_len)) + conn_params.authorized = true; + cfg80211_connect_done(ndev, &conn_params, GFP_KERNEL); brcmf_dbg(CONN, "Report connect result - connection %s\n", completed ? "succeeded" : "failed"); @@ -6052,6 +7020,14 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, } if (brcmf_is_apmode(ifp->vif)) { + if (e->event_code == BRCMF_E_ASSOC_IND || + e->event_code == BRCMF_E_REASSOC_IND) { + brcmf_findadd_sta(ifp, e->addr); + } else if ((e->event_code == BRCMF_E_DISASSOC_IND) || + (e->event_code == BRCMF_E_DEAUTH_IND) || + (e->event_code == BRCMF_E_DEAUTH)) { + brcmf_del_sta(ifp, e->addr); + } err = brcmf_notify_connect_status_ap(cfg, ndev, e, data); } else if (brcmf_is_linkup(ifp->vif, e)) { brcmf_dbg(CONN, "Linkup\n"); @@ -6070,9 +7046,13 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, } else if (brcmf_is_linkdown(e)) { brcmf_dbg(CONN, "Linkdown\n"); if (!brcmf_is_ibssmode(ifp->vif) && - test_bit(BRCMF_VIF_STATUS_CONNECTED, - &ifp->vif->sme_state)) { - if (memcmp(profile->bssid, e->addr, ETH_ALEN)) + (test_bit(BRCMF_VIF_STATUS_CONNECTED, + &ifp->vif->sme_state) || + test_bit(BRCMF_VIF_STATUS_CONNECTING, + &ifp->vif->sme_state))) { + if (test_bit(BRCMF_VIF_STATUS_CONNECTED, + &ifp->vif->sme_state) && + memcmp(profile->bssid, e->addr, ETH_ALEN)) return err; brcmf_bss_connect_done(cfg, ndev, e, false); @@ -6144,6 +7124,9 @@ static s32 brcmf_notify_vif_event(struct brcmf_if *ifp, struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data; struct brcmf_cfg80211_vif_event *event = &cfg->vif_event; struct brcmf_cfg80211_vif *vif; + enum nl80211_iftype iftype = NL80211_IFTYPE_UNSPECIFIED; + bool vif_pend = false; + int err; brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfgidx %u\n", ifevent->action, ifevent->flags, ifevent->ifidx, @@ -6156,9 +7139,28 @@ static s32 brcmf_notify_vif_event(struct brcmf_if *ifp, switch (ifevent->action) { case BRCMF_E_IF_ADD: /* waiting process may have timed out */ - if (!cfg->vif_event.vif) { + if (!vif) { + /* handle IF_ADD event from firmware */ spin_unlock(&event->vif_event_lock); - return -EBADF; + vif_pend = true; + if (ifevent->role == WLC_E_IF_ROLE_STA) + iftype = NL80211_IFTYPE_STATION; + else if (ifevent->role == WLC_E_IF_ROLE_AP) + iftype = NL80211_IFTYPE_AP; + else + vif_pend = false; + + if (vif_pend) { + vif = brcmf_alloc_vif(cfg, iftype); + if (IS_ERR(vif)) { + brcmf_err("Role:%d failed to alloc vif\n", + ifevent->role); + return PTR_ERR(vif); + } + } else { + brcmf_err("Invalid Role:%d\n", ifevent->role); + return -EBADF; + } } ifp->vif = vif; @@ -6168,6 +7170,18 @@ static s32 brcmf_notify_vif_event(struct brcmf_if *ifp, ifp->ndev->ieee80211_ptr = &vif->wdev; SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy)); } + + if (vif_pend) { + err = brcmf_net_attach(ifp, false); + if (err) { + brcmf_err("netdevice register failed with err:%d\n", + err); + brcmf_free_vif(vif); + free_netdev(ifp->ndev); + } + return err; + } + spin_unlock(&event->vif_event_lock); wake_up(&event->vif_wq); return 0; @@ -6191,6 +7205,182 @@ static s32 brcmf_notify_vif_event(struct brcmf_if *ifp, return -EINVAL; } +static s32 +brcmf_notify_ext_auth_request(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + struct brcmf_pub *drvr = ifp->drvr; + struct cfg80211_external_auth_params params; + struct brcmf_auth_req_status_le *auth_req = + (struct brcmf_auth_req_status_le *)data; + s32 err = 0; + + brcmf_dbg(INFO, "Enter: event %s (%d) received\n", + brcmf_fweh_event_name(e->event_code), e->event_code); + + if (e->datalen < sizeof(*auth_req)) { + bphy_err(drvr, "Event %s (%d) data too small. Ignore\n", + brcmf_fweh_event_name(e->event_code), e->event_code); + return -EINVAL; + } + + memset(¶ms, 0, sizeof(params)); + params.action = NL80211_EXTERNAL_AUTH_START; + params.key_mgmt_suite = ntohl(WLAN_AKM_SUITE_SAE); + params.status = WLAN_STATUS_SUCCESS; + params.ssid.ssid_len = min_t(u32, 32, le32_to_cpu(auth_req->ssid_len)); + memcpy(params.ssid.ssid, auth_req->ssid, params.ssid.ssid_len); + memcpy(params.bssid, auth_req->peer_mac, ETH_ALEN); + + err = cfg80211_external_auth_request(ifp->ndev, ¶ms, GFP_ATOMIC); + if (err) + bphy_err(drvr, "Ext Auth request to supplicant failed (%d)\n", + err); + + return err; +} + +static s32 +brcmf_notify_auth_frame_rx(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + struct brcmf_pub *drvr = ifp->drvr; + struct brcmf_cfg80211_info *cfg = drvr->config; + struct wireless_dev *wdev; + u32 mgmt_frame_len = e->datalen - sizeof(struct brcmf_rx_mgmt_data); + struct brcmf_rx_mgmt_data *rxframe = (struct brcmf_rx_mgmt_data *)data; + u8 *frame = (u8 *)(rxframe + 1); + struct brcmu_chan ch; + struct ieee80211_mgmt *mgmt_frame; + s32 freq; + + brcmf_dbg(INFO, "Enter: event %s (%d) received\n", + brcmf_fweh_event_name(e->event_code), e->event_code); + + if (e->datalen < sizeof(*rxframe)) { + bphy_err(drvr, "Event %s (%d) data too small. Ignore\n", + brcmf_fweh_event_name(e->event_code), e->event_code); + return -EINVAL; + } + + wdev = &ifp->vif->wdev; + WARN_ON(!wdev); + + ch.chspec = be16_to_cpu(rxframe->chanspec); + cfg->d11inf.decchspec(&ch); + + mgmt_frame = kzalloc(mgmt_frame_len, GFP_KERNEL); + if (!mgmt_frame) + return -ENOMEM; + + mgmt_frame->frame_control = cpu_to_le16(IEEE80211_STYPE_AUTH); + memcpy(mgmt_frame->da, ifp->mac_addr, ETH_ALEN); + memcpy(mgmt_frame->sa, e->addr, ETH_ALEN); + brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSSID, mgmt_frame->bssid, + ETH_ALEN); + frame += offsetof(struct ieee80211_mgmt, u); + memcpy(&mgmt_frame->u, frame, + mgmt_frame_len - offsetof(struct ieee80211_mgmt, u)); + + freq = ieee80211_channel_to_frequency(ch.control_ch_num, + BRCMU_CHAN_BAND_TO_NL80211(ch.band)); + + cfg80211_rx_mgmt(wdev, freq, 0, (u8 *)mgmt_frame, mgmt_frame_len, + NL80211_RXMGMT_FLAG_EXTERNAL_AUTH); + kfree(mgmt_frame); + return 0; +} + +static s32 +brcmf_notify_mgmt_tx_status(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + struct brcmf_cfg80211_vif *vif = ifp->vif; + u32 *packet_id = (u32 *)data; + + brcmf_dbg(INFO, "Enter: event %s (%d), status=%d\n", + brcmf_fweh_event_name(e->event_code), e->event_code, + e->status); + + if (!test_bit(BRCMF_MGMT_TX_SEND_FRAME, &vif->mgmt_tx_status) || + (*packet_id != vif->mgmt_tx_id)) + return 0; + + if (e->event_code == BRCMF_E_MGMT_FRAME_TXSTATUS) { + if (e->status == BRCMF_E_STATUS_SUCCESS) + set_bit(BRCMF_MGMT_TX_ACK, &vif->mgmt_tx_status); + else + set_bit(BRCMF_MGMT_TX_NOACK, &vif->mgmt_tx_status); + } else { + set_bit(BRCMF_MGMT_TX_OFF_CHAN_COMPLETED, &vif->mgmt_tx_status); + } + + complete(&vif->mgmt_tx); + return 0; +} + +static s32 +brcmf_notify_rssi_change_ind(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + struct wl_event_data_rssi *value = (struct wl_event_data_rssi *)data; + s32 rssi = 0; + + brcmf_dbg(INFO, "Enter: event %s (%d), status=%d\n", + brcmf_fweh_event_name(e->event_code), e->event_code, + e->status); + + if (!cfg->cqm_info.enable) + return 0; + + rssi = ntohl(value->rssi); + brcmf_dbg(TRACE, "rssi: %d, threshold: %d, send event(%s)\n", + rssi, cfg->cqm_info.rssi_threshold, + rssi > cfg->cqm_info.rssi_threshold ? "HIGH" : "LOW"); + cfg80211_cqm_rssi_notify(cfg_to_ndev(cfg), + (rssi > cfg->cqm_info.rssi_threshold ? + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH : + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW), + rssi, GFP_KERNEL); + + return 0; +} + +static s32 +brcmf_notify_beacon_loss(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; + struct cfg80211_bss *bss; + + brcmf_dbg(INFO, "Enter: event %s (%d), status=%d\n", + brcmf_fweh_event_name(e->event_code), e->event_code, + e->status); + + if (!ifp->drvr->settings->roamoff) + return 0; + + /* On beacon loss event, Supplicant triggers new scan request + * with NL80211_SCAN_FLAG_FLUSH Flag set, but lost AP bss entry + * still remained as it is held by cfg as associated. Unlinking this + * current BSS from cfg cached bss list on beacon loss event here, + * would allow supplicant to receive new scanned entries + * without current bss and select new bss to trigger roam. + */ + bss = cfg80211_get_bss(cfg->wiphy, NULL, profile->bssid, 0, 0, + IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY); + if (bss) { + cfg80211_unlink_bss(cfg->wiphy, bss); + cfg80211_put_bss(cfg->wiphy, bss); + } + + cfg80211_cqm_beacon_loss_notify(cfg_to_ndev(cfg), GFP_KERNEL); + + return 0; +} + static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf) { conf->frag_threshold = (u32)-1; @@ -6235,6 +7425,18 @@ static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg) brcmf_p2p_notify_action_tx_complete); brcmf_fweh_register(cfg->pub, BRCMF_E_PSK_SUP, brcmf_notify_connect_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_EXT_AUTH_REQ, + brcmf_notify_ext_auth_request); + brcmf_fweh_register(cfg->pub, BRCMF_E_EXT_AUTH_FRAME_RX, + brcmf_notify_auth_frame_rx); + brcmf_fweh_register(cfg->pub, BRCMF_E_MGMT_FRAME_TXSTATUS, + brcmf_notify_mgmt_tx_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_MGMT_FRAME_OFF_CHAN_COMPLETE, + brcmf_notify_mgmt_tx_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_RSSI, + brcmf_notify_rssi_change_ind); + brcmf_fweh_register(cfg->pub, BRCMF_E_BCNLOST_MSG, + brcmf_notify_beacon_loss); } static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg) @@ -6303,6 +7505,7 @@ static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg) cfg->dongle_up = false; /* dongle down */ brcmf_abort_scanning(cfg); brcmf_deinit_priv_mem(cfg); + brcmf_clear_assoc_ies(cfg); } static void init_vif_event(struct brcmf_cfg80211_vif_event *event) @@ -6420,15 +7623,14 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, struct brcmf_pub *drvr = cfg->pub; struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0); struct ieee80211_supported_band *band; - struct ieee80211_channel *channel; + struct ieee80211_channel *channel, *cur, *next; struct brcmf_chanspec_list *list; struct brcmu_chan ch; int err; u8 *pbuf; u32 i, j; u32 total; - u32 chaninfo; - + u32 chaninfo, n_2g = 0, n_5g = 0, n_6g = 0; pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); if (pbuf == NULL) @@ -6448,6 +7650,10 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, for (i = 0; i < band->n_channels; i++) band->channels[i].flags = IEEE80211_CHAN_DISABLED; band = wiphy->bands[NL80211_BAND_5GHZ]; + if (band) + for (i = 0; i < band->n_channels; i++) + band->channels[i].flags = IEEE80211_CHAN_DISABLED; + band = wiphy->bands[NL80211_BAND_6GHZ]; if (band) for (i = 0; i < band->n_channels; i++) band->channels[i].flags = IEEE80211_CHAN_DISABLED; @@ -6461,6 +7667,8 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, band = wiphy->bands[NL80211_BAND_2GHZ]; } else if (ch.band == BRCMU_CHAN_BAND_5G) { band = wiphy->bands[NL80211_BAND_5GHZ]; + } else if (ch.band == BRCMU_CHAN_BAND_6G) { + band = wiphy->bands[NL80211_BAND_6GHZ]; } else { bphy_err(drvr, "Invalid channel Spec. 0x%x.\n", ch.chspec); @@ -6536,6 +7744,62 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, } } + /* Remove disabled channels to avoid unexpected restore. */ + band = wiphy->bands[NL80211_BAND_2GHZ]; + if (band) { + n_2g = band->n_channels; + for (i = 0; i < n_2g;) { + cur = &band->channels[i]; + if (cur->flags == IEEE80211_CHAN_DISABLED) { + for (j = i; j < n_2g - 1; j++) { + cur = &band->channels[j]; + next = &band->channels[j + 1]; + memcpy(cur, next, sizeof(*cur)); + } + n_2g--; + } else + i++; + } + wiphy->bands[NL80211_BAND_2GHZ]->n_channels = n_2g; + } + band = wiphy->bands[NL80211_BAND_5GHZ]; + if (band) { + n_5g = band->n_channels; + for (i = 0; i < n_5g;) { + cur = &band->channels[i]; + if (cur->flags == IEEE80211_CHAN_DISABLED) { + for (j = i; j < n_5g - 1; j++) { + cur = &band->channels[j]; + next = &band->channels[j + 1]; + memcpy(cur, next, sizeof(*cur)); + } + n_5g--; + } else + i++; + } + wiphy->bands[NL80211_BAND_5GHZ]->n_channels = n_5g; + } + band = wiphy->bands[NL80211_BAND_6GHZ]; + if (band) { + n_6g = band->n_channels; + for (i = 0; i < n_6g;) { + cur = &band->channels[i]; + if (cur->flags == IEEE80211_CHAN_DISABLED) { + for (j = i; j < n_6g - 1; j++) { + cur = &band->channels[j]; + next = &band->channels[j + 1]; + memcpy(cur, next, sizeof(*cur)); + } + /* To avoid fw crash while delete all channels */ + if (n_6g == 1) + break; + n_6g--; + } else + i++; + } + wiphy->bands[NL80211_BAND_6GHZ]->n_channels = n_6g; + } + fail_pbuf: kfree(pbuf); return err; @@ -6633,6 +7897,12 @@ static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[]) err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band); if (!err) { bw_cap[NL80211_BAND_5GHZ] = band; + band = WLC_BAND_6G; + err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band); + if (!err) { + bw_cap[NL80211_BAND_6GHZ] = band; + return; + } return; } WARN_ON(1); @@ -6664,6 +7934,10 @@ static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[]) static void brcmf_update_ht_cap(struct ieee80211_supported_band *band, u32 bw_cap[2], u32 nchain) { + /* not allowed in 6G band */ + if (band->band == NL80211_BAND_6GHZ) + return; + band->ht_cap.ht_supported = true; if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) { band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; @@ -6694,8 +7968,8 @@ static void brcmf_update_vht_cap(struct ieee80211_supported_band *band, { __le16 mcs_map; - /* not allowed in 2.4G band */ - if (band->band == NL80211_BAND_2GHZ) + /* not allowed in 2.4G & 6G band */ + if (band->band == NL80211_BAND_2GHZ || band->band == NL80211_BAND_6GHZ) return; band->vht_cap.vht_supported = true; @@ -6730,6 +8004,86 @@ static void brcmf_update_vht_cap(struct ieee80211_supported_band *band, } } +static void brcmf_update_he_cap(struct ieee80211_supported_band *band, + struct ieee80211_sband_iftype_data *data) +{ + int idx = 1; + struct ieee80211_sta_he_cap *he_cap = &data->he_cap; + struct ieee80211_he_cap_elem *he_cap_elem = &he_cap->he_cap_elem; + struct ieee80211_he_mcs_nss_supp *he_mcs = &he_cap->he_mcs_nss_supp; + + if (data == NULL) { + brcmf_dbg(INFO, "failed to allco mem\n"); + return; + } + + data->types_mask = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP); + he_cap->has_he = true; + + /* HE MAC Capabilities Information */ + he_cap_elem->mac_cap_info[0] = IEEE80211_HE_MAC_CAP0_HTC_HE | + IEEE80211_HE_MAC_CAP0_TWT_REQ | + IEEE80211_HE_MAC_CAP0_TWT_RES; + + he_cap_elem->mac_cap_info[1] = IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_8US | + IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US; + + he_cap_elem->mac_cap_info[2] = IEEE80211_HE_MAC_CAP2_BSR | + IEEE80211_HE_MAC_CAP2_BCAST_TWT; + + he_cap_elem->mac_cap_info[3] = IEEE80211_HE_MAC_CAP3_OMI_CONTROL | + IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_VHT_1 | + IEEE80211_HE_MAC_CAP3_FLEX_TWT_SCHED; + + he_cap_elem->mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMDSU_IN_AMPDU; + + + /* HE PHY Capabilities Information */ + he_cap_elem->phy_cap_info[0] = IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G; + + he_cap_elem->phy_cap_info[1] = IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD; + + he_cap_elem->phy_cap_info[2] = IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US | + IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO | + IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO; + + he_cap_elem->phy_cap_info[3] = IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_QPSK | + IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_2 | + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM; + + he_cap_elem->phy_cap_info[4] = IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE | + IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_8; + + he_cap_elem->phy_cap_info[5] = IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK | + IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK; + + he_cap_elem->phy_cap_info[6] = IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU | + IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU | + IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMER_FB | + IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB | + IEEE80211_HE_PHY_CAP6_TRIG_CQI_FB | + IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE | + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT; + + he_cap_elem->phy_cap_info[7] = IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI | + IEEE80211_HE_PHY_CAP7_MAX_NC_1; + + he_cap_elem->phy_cap_info[8] = IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI | + IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G; + + he_cap_elem->phy_cap_info[9] = IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU | + IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB | + IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB; + + /* HE Supported MCS and NSS Set */ + he_mcs->rx_mcs_80 = cpu_to_le16(0xfffa); + he_mcs->tx_mcs_80 = cpu_to_le16(0xfffa); + + band->n_iftype_data = idx; + band->iftype_data = data; +} + static int brcmf_setup_wiphybands(struct brcmf_cfg80211_info *cfg) { struct brcmf_pub *drvr = cfg->pub; @@ -6737,7 +8091,10 @@ static int brcmf_setup_wiphybands(struct brcmf_cfg80211_info *cfg) struct wiphy *wiphy = cfg_to_wiphy(cfg); u32 nmode = 0; u32 vhtmode = 0; - u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT }; + u32 bw_cap[4] = { WLC_BW_20MHZ_BIT, /* 2GHz */ + WLC_BW_20MHZ_BIT, /* 5GHz */ + 0, /* 60GHz */ + WLC_BW_20MHZ_BIT };/* 6GHz */ u32 rxchain; u32 nchain; int err; @@ -6746,6 +8103,7 @@ static int brcmf_setup_wiphybands(struct brcmf_cfg80211_info *cfg) u32 txstreams = 0; u32 txbf_bfe_cap = 0; u32 txbf_bfr_cap = 0; + u32 he[2] = {0, 0}; (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode); err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode); @@ -6754,8 +8112,10 @@ static int brcmf_setup_wiphybands(struct brcmf_cfg80211_info *cfg) } else { brcmf_get_bwcap(ifp, bw_cap); } - brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n", - nmode, vhtmode, bw_cap[NL80211_BAND_2GHZ], + (void)brcmf_fil_iovar_data_get(ifp, "he", he, sizeof(he)); + + brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, he=%d, bw_cap=(%d, %d)\n", + nmode, vhtmode, he[0], bw_cap[NL80211_BAND_2GHZ], bw_cap[NL80211_BAND_5GHZ]); err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain); @@ -6792,6 +8152,8 @@ static int brcmf_setup_wiphybands(struct brcmf_cfg80211_info *cfg) if (vhtmode) brcmf_update_vht_cap(band, bw_cap, nchain, txstreams, txbf_bfe_cap, txbf_bfr_cap); + if (he[0]) + brcmf_update_he_cap(band, &sdata[band->band]); } return 0; @@ -6802,6 +8164,7 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = { [NL80211_IFTYPE_STATION] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_AUTH >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) }, [NL80211_IFTYPE_P2P_CLIENT] = { @@ -6865,7 +8228,7 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = { * * p2p, mchan, and mbss: * - * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total + * #STA <= 2, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total * #AP <= 4, matching BI, channels = 1, 4 total * @@ -6911,7 +8274,7 @@ static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp) goto err; combo[c].num_different_channels = 1 + (rsdb || (p2p && mchan)); - c0_limits[i].max = 1; + c0_limits[i].max = 1 + (p2p && mchan); c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION); if (mon_flag) { c0_limits[i].max = 1; @@ -7011,6 +8374,7 @@ static void brcmf_wiphy_wowl_params(struct wiphy *wiphy, struct brcmf_if *ifp) struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_pub *drvr = cfg->pub; struct wiphy_wowlan_support *wowl; + struct cfg80211_wowlan *brcmf_wowlan_config = NULL; wowl = kmemdup(&brcmf_wowlan_support, sizeof(brcmf_wowlan_support), GFP_KERNEL); @@ -7033,17 +8397,39 @@ static void brcmf_wiphy_wowl_params(struct wiphy *wiphy, struct brcmf_if *ifp) } wiphy->wowlan = wowl; + + /* wowlan_config structure report for kernels */ + brcmf_wowlan_config = kzalloc(sizeof(*brcmf_wowlan_config), + GFP_KERNEL); + if (brcmf_wowlan_config) { + brcmf_wowlan_config->any = false; + brcmf_wowlan_config->disconnect = true; + brcmf_wowlan_config->eap_identity_req = true; + brcmf_wowlan_config->four_way_handshake = true; + brcmf_wowlan_config->rfkill_release = false; + brcmf_wowlan_config->patterns = NULL; + brcmf_wowlan_config->n_patterns = 0; + brcmf_wowlan_config->tcp = NULL; + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK)) + brcmf_wowlan_config->gtk_rekey_failure = true; + else + brcmf_wowlan_config->gtk_rekey_failure = false; + } else { + brcmf_err("Can not allocate memory for brcm_wowlan_config\n"); + } + wiphy->wowlan_config = brcmf_wowlan_config; #endif } static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) { struct brcmf_pub *drvr = ifp->drvr; + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); const struct ieee80211_iface_combination *combo; struct ieee80211_supported_band *band; u16 max_interfaces = 0; bool gscan; - __le32 bandlist[3]; + __le32 bandlist[4]; u32 n_bands; int err, i; @@ -7077,7 +8463,7 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) wiphy->cipher_suites = brcmf_cipher_suites; wiphy->n_cipher_suites = ARRAY_SIZE(brcmf_cipher_suites); if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP)) - wiphy->n_cipher_suites--; + wiphy->n_cipher_suites -= 4; wiphy->bss_select_support = BIT(NL80211_BSS_SELECT_ATTR_RSSI) | BIT(NL80211_BSS_SELECT_ATTR_BAND_PREF) | BIT(NL80211_BSS_SELECT_ATTR_RSSI_ADJUST); @@ -7107,6 +8493,17 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SAE_OFFLOAD_AP); } + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE_EXT)) { + wiphy->features |= NL80211_FEATURE_SAE; + wiphy_ext_feature_set(wiphy, + NL80211_EXT_FEATURE_AP_PMKSA_CACHING); + } + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FBT) || + brcmf_feat_is_enabled(ifp, BRCMF_FEAT_OKC)) + wiphy_ext_feature_set(wiphy, + NL80211_EXT_FEATURE_ROAM_OFFLOAD); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + wiphy->mgmt_stypes = brcmf_txrx_stypes; wiphy->max_remain_on_channel_duration = 5000; if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) { @@ -7116,9 +8513,12 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) /* vendor commands/events support */ wiphy->vendor_commands = brcmf_vendor_cmds; wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1; + wiphy->vendor_events = brcmf_vendor_events; + wiphy->n_vendor_events = BRCMF_VNDR_EVTS_LAST; + brcmf_fweh_register(cfg->pub, BRCMF_E_PHY_TEMP, + brcmf_wiphy_phy_temp_evt_handler); - if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) - brcmf_wiphy_wowl_params(wiphy, ifp); + brcmf_wiphy_wowl_params(wiphy, ifp); err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist, sizeof(bandlist)); if (err) { @@ -7162,6 +8562,23 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) band->n_channels = ARRAY_SIZE(__wl_5ghz_channels); wiphy->bands[NL80211_BAND_5GHZ] = band; } + if (bandlist[i] == cpu_to_le32(WLC_BAND_6G)) { + band = kmemdup(&__wl_band_6ghz, sizeof(__wl_band_6ghz), + GFP_KERNEL); + if (!band) + return -ENOMEM; + + band->channels = kmemdup(&__wl_6ghz_channels, + sizeof(__wl_6ghz_channels), + GFP_KERNEL); + if (!band->channels) { + kfree(band); + return -ENOMEM; + } + + band->n_channels = ARRAY_SIZE(__wl_6ghz_channels); + wiphy->bands[NL80211_BAND_6GHZ] = band; + } } if (wiphy->bands[NL80211_BAND_5GHZ] && @@ -7170,7 +8587,6 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) NL80211_EXT_FEATURE_DFS_OFFLOAD); wiphy_read_of_freq_limits(wiphy); - return 0; } @@ -7181,6 +8597,7 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) struct wireless_dev *wdev; struct brcmf_if *ifp; s32 power_mode; + s32 eap_restrict; s32 err = 0; if (cfg->dongle_up) @@ -7195,7 +8612,7 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) brcmf_dongle_scantime(ifp); - power_mode = cfg->pwr_save ? PM_FAST : PM_OFF; + power_mode = cfg->pwr_save ? ifp->drvr->settings->default_pm : PM_OFF; err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode); if (err) goto default_conf_out; @@ -7205,6 +8622,14 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) err = brcmf_dongle_roam(ifp); if (err) goto default_conf_out; + + eap_restrict = ifp->drvr->settings->eap_restrict; + if (eap_restrict) { + err = brcmf_fil_iovar_int_set(ifp, "eap_restrict", + eap_restrict); + if (err) + brcmf_info("eap_restrict error (%d)\n", err); + } err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype, NULL); if (err) @@ -7248,6 +8673,7 @@ static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp) the state fw and WPA_Supplicant state consistent */ brcmf_delay(500); + cfg->dongle_up = false; } brcmf_abort_scanning(cfg); @@ -7390,6 +8816,229 @@ static s32 brcmf_translate_country_code(struct brcmf_pub *drvr, char alpha2[2], return 0; } +static int +brcmf_parse_dump_obss(char *buf, struct brcmf_dump_survey *survey) +{ + int i; + char *token; + char delim[] = "\n "; + unsigned long val; + int err = 0; + + token = strsep(&buf, delim); + while (token) { + if (!strcmp(token, "OBSS")) { + for (i = 0; i < OBSS_TOKEN_IDX; i++) + token = strsep(&buf, delim); + err = kstrtoul(token, 10, &val); + survey->obss = val; + } + + if (!strcmp(token, "IBSS")) { + for (i = 0; i < IBSS_TOKEN_IDX; i++) + token = strsep(&buf, delim); + err = kstrtoul(token, 10, &val); + survey->ibss = val; + } + + if (!strcmp(token, "TXDur")) { + for (i = 0; i < TX_TOKEN_IDX; i++) + token = strsep(&buf, delim); + err = kstrtoul(token, 10, &val); + survey->tx = val; + } + + if (!strcmp(token, "Category")) { + for (i = 0; i < CTG_TOKEN_IDX; i++) + token = strsep(&buf, delim); + err = kstrtoul(token, 10, &val); + survey->no_ctg = val; + } + + if (!strcmp(token, "Packet")) { + for (i = 0; i < PKT_TOKEN_IDX; i++) + token = strsep(&buf, delim); + err = kstrtoul(token, 10, &val); + survey->no_pckt = val; + } + + if (!strcmp(token, "Opp(time):")) { + for (i = 0; i < IDLE_TOKEN_IDX; i++) + token = strsep(&buf, delim); + err = kstrtoul(token, 10, &val); + survey->idle = val; + } + + token = strsep(&buf, delim); + + if (err) + return err; + } + + return 0; +} + +static int +brcmf_dump_obss(struct brcmf_if *ifp, struct cca_msrmnt_query req, + struct brcmf_dump_survey *survey) +{ + struct cca_stats_n_flags *results; + char *buf; + int err; + + buf = kzalloc(sizeof(char) * BRCMF_DCMD_MEDLEN, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + memcpy(buf, &req, sizeof(struct cca_msrmnt_query)); + err = brcmf_fil_iovar_data_get(ifp, "dump_obss", + buf, BRCMF_DCMD_MEDLEN); + if (err) { + brcmf_err("dump_obss error (%d)\n", err); + err = -EINVAL; + goto exit; + } + results = (struct cca_stats_n_flags *)(buf); + + if (req.msrmnt_query) + brcmf_parse_dump_obss(results->buf, survey); + +exit: + kfree(buf); + return err; +} + +static s32 +cfg80211_set_channel(struct wiphy *wiphy, struct net_device *dev, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type) +{ + u16 chspec = 0; + int err = 0; + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); + + /* set_channel */ + chspec = channel_to_chanspec(&cfg->d11inf, chan); + if (chspec != INVCHANSPEC) { + err = brcmf_fil_iovar_int_set(ifp, "chanspec", chspec); + if (err) + err = -EINVAL; + } else { + brcmf_err("failed to convert host chanspec to fw chanspec\n"); + err = -EINVAL; + } + + return err; +} + +static int +brcmf_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *ndev, + int idx, struct survey_info *info) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); + struct brcmf_dump_survey *survey; + struct ieee80211_supported_band *band; + struct ieee80211_channel *chan; + struct cca_msrmnt_query req; + u32 noise; + int err; + + brcmf_dbg(TRACE, "Enter: channel idx=%d\n", idx); + + if (!brcmf_is_apmode(ifp->vif)) + return -ENOENT; + + /* Do not run survey when VIF in CONNECTING / CONNECTED states */ + if ((test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) || + (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))) { + return -EBUSY; + } + + band = wiphy->bands[NL80211_BAND_2GHZ]; + if (band && idx >= band->n_channels) { + idx -= band->n_channels; + band = NULL; + } + + if (!band || idx >= band->n_channels) { + band = wiphy->bands[NL80211_BAND_5GHZ]; + if (idx >= band->n_channels) + return -ENOENT; + } + + /* Setting current channel to the requested channel */ + chan = &band->channels[idx]; + err = cfg80211_set_channel(wiphy, ndev, chan, NL80211_CHAN_HT20); + if (err) { + info->channel = chan; + info->filled = 0; + return 0; + } + + survey = kzalloc(sizeof(*survey), GFP_KERNEL); + if (!survey) + return -ENOMEM; + + /* Disable mpc */ + brcmf_set_mpc(ifp, 0); + + /* Set interface up, explicitly. */ + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1); + if (err) { + brcmf_err("set interface up failed, err = %d\n", err); + goto exit; + } + + /* Get noise value */ + err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PHY_NOISE, &noise); + if (err) { + brcmf_err("Get Phy Noise failed, use dummy value\n"); + noise = CHAN_NOISE_DUMMY; + } + + + /* Start Measurement for obss stats on current channel */ + req.msrmnt_query = 0; + req.time_req = ACS_MSRMNT_DELAY; + err = brcmf_dump_obss(ifp, req, survey); + if (err) + goto exit; + + /* Add 10 ms for IOVAR completion */ + msleep(ACS_MSRMNT_DELAY + 10); + + /* Issue IOVAR to collect measurement results */ + req.msrmnt_query = 1; + err = brcmf_dump_obss(ifp, req, survey); + if (err) + goto exit; + + info->channel = chan; + info->noise = noise; + info->time = ACS_MSRMNT_DELAY; + info->time_busy = ACS_MSRMNT_DELAY - survey->idle; + info->time_rx = survey->obss + survey->ibss + survey->no_ctg + + survey->no_pckt; + info->time_tx = survey->tx; + info->filled = SURVEY_INFO_NOISE_DBM | SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY | SURVEY_INFO_TIME_RX | + SURVEY_INFO_TIME_TX; + + brcmf_dbg(INFO, "OBSS dump: channel %d: survey duration %d\n", + ieee80211_frequency_to_channel(chan->center_freq), + ACS_MSRMNT_DELAY); + brcmf_dbg(INFO, "noise(%d) busy(%llu) rx(%llu) tx(%llu)\n", + info->noise, info->time_busy, info->time_rx, info->time_tx); + +exit: + if (!brcmf_is_apmode(ifp->vif)) + brcmf_set_mpc(ifp, 1); + kfree(survey); + return err; +} + static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy, struct regulatory_request *req) { @@ -7453,6 +9102,11 @@ static void brcmf_free_wiphy(struct wiphy *wiphy) kfree(wiphy->bands[NL80211_BAND_5GHZ]->channels); kfree(wiphy->bands[NL80211_BAND_5GHZ]); } + if (wiphy->bands[NL80211_BAND_6GHZ]) { + kfree(wiphy->bands[NL80211_BAND_6GHZ]->channels); + kfree(wiphy->bands[NL80211_BAND_6GHZ]); + } + #if IS_ENABLED(CONFIG_PM) if (wiphy->wowlan != &brcmf_wowlan_support) kfree(wiphy->wowlan); @@ -7485,6 +9139,8 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, cfg->wiphy = wiphy; cfg->pub = drvr; + cfg->pm_state = BRCMF_CFG80211_PM_STATE_RESUMED; + cfg->num_softap = 0; init_vif_event(&cfg->vif_event); INIT_LIST_HEAD(&cfg->vif_list); @@ -7541,11 +9197,10 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK)) ops->set_rekey_data = brcmf_cfg80211_set_rekey_data; #endif - err = wiphy_register(wiphy); - if (err < 0) { - bphy_err(drvr, "Could not register wiphy device (%d)\n", err); - goto priv_out; - } + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_DUMP_OBSS)) + ops->dump_survey = brcmf_cfg80211_dump_survey; + else + ops->dump_survey = NULL; err = brcmf_setup_wiphybands(cfg); if (err) { @@ -7553,6 +9208,12 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, goto wiphy_unreg_out; } + err = wiphy_register(wiphy); + if (err < 0) { + bphy_err(drvr, "Could not register wiphy device (%d)\n", err); + goto priv_out; + } + /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(), * setup 40MHz in 2GHz band and enable OBSS scanning. */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h index 17817cdb5de26..715bd43bc003c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h @@ -92,6 +92,15 @@ #define BRCMF_VIF_EVENT_TIMEOUT msecs_to_jiffies(1500) +#define BRCMF_PM_WAIT_MAXRETRY 100 + +/* cfg80211 wowlan definitions */ +#define WL_WOWLAN_MAX_PATTERNS 8 +#define WL_WOWLAN_MIN_PATTERN_LEN 1 +#define WL_WOWLAN_MAX_PATTERN_LEN 255 +#define WL_WOWLAN_PKT_FILTER_ID_FIRST 201 +#define WL_WOWLAN_PKT_FILTER_ID_LAST (WL_WOWLAN_PKT_FILTER_ID_FIRST + \ + WL_WOWLAN_MAX_PATTERNS - 1) /** * enum brcmf_scan_status - scan engine status * @@ -125,7 +134,8 @@ enum brcmf_profile_fwsup { BRCMF_PROFILE_FWSUP_NONE, BRCMF_PROFILE_FWSUP_PSK, BRCMF_PROFILE_FWSUP_1X, - BRCMF_PROFILE_FWSUP_SAE + BRCMF_PROFILE_FWSUP_SAE, + BRCMF_PROFILE_FWSUP_ROAM }; /** @@ -155,6 +165,7 @@ struct brcmf_cfg80211_profile { enum brcmf_profile_fwsup use_fwsup; u16 use_fwauth; bool is_ft; + bool is_okc; }; /** @@ -178,6 +189,28 @@ enum brcmf_vif_status { BRCMF_VIF_STATUS_ASSOC_SUCCESS, }; +enum brcmf_cfg80211_pm_state { + BRCMF_CFG80211_PM_STATE_RESUMED, + BRCMF_CFG80211_PM_STATE_RESUMING, + BRCMF_CFG80211_PM_STATE_SUSPENDED, + BRCMF_CFG80211_PM_STATE_SUSPENDING, +}; + +/** + * enum brcmf_mgmt_tx_status - mgmt frame tx status + * + * @BRCMF_MGMT_TX_ACK: mgmt frame acked + * @BRCMF_MGMT_TX_NOACK: mgmt frame not acked + * @BRCMF_MGMT_TX_OFF_CHAN_COMPLETED: off-channel complete + * @BRCMF_MGMT_TX_SEND_FRAME: mgmt frame tx is in progres + */ +enum brcmf_mgmt_tx_status { + BRCMF_MGMT_TX_ACK, + BRCMF_MGMT_TX_NOACK, + BRCMF_MGMT_TX_OFF_CHAN_COMPLETED, + BRCMF_MGMT_TX_SEND_FRAME +}; + /** * struct vif_saved_ie - holds saved IEs for a virtual interface. * @@ -221,6 +254,9 @@ struct brcmf_cfg80211_vif { unsigned long sme_state; struct vif_saved_ie saved_ie; struct list_head list; + struct completion mgmt_tx; + unsigned long mgmt_tx_status; + u32 mgmt_tx_id; u16 mgmt_rx_reg; bool mbss; int is_11d; @@ -261,6 +297,11 @@ struct escan_info { struct cfg80211_scan_request *request); }; +struct cqm_rssi_info { + bool enable; + s32 rssi_threshold; +}; + /** * struct brcmf_cfg80211_vif_event - virtual interface event information. * @@ -357,6 +398,7 @@ struct brcmf_cfg80211_info { struct escan_info escan_info; struct timer_list escan_timeout; struct work_struct escan_timeout_work; + struct cqm_rssi_info cqm_info; struct list_head vif_list; struct brcmf_cfg80211_vif_event vif_event; struct completion vif_disabled; @@ -365,6 +407,8 @@ struct brcmf_cfg80211_info { struct brcmf_cfg80211_wowl wowl; struct brcmf_pno_info *pno; u8 ac_priority[MAX_8021D_PRIO]; + u8 pm_state; + u8 num_softap; }; /** @@ -461,5 +505,6 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, void brcmf_set_mpc(struct brcmf_if *ndev, int mpc); void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg); void brcmf_cfg80211_free_netdev(struct net_device *ndev); - +bool brcmf_is_apmode_operating(struct wiphy *wiphy); +void brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev); #endif /* BRCMFMAC_CFG80211_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c index 5bf11e46fc49a..2807530016ced 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c @@ -213,6 +213,50 @@ struct sbsocramregs { #define ARMCR4_BSZ_MASK 0x3f #define ARMCR4_BSZ_MULT 8192 +/* Minimum PMU resource mask for 43012C0 */ +#define CY_43012_PMU_MIN_RES_MASK 0xF8BFE77 + +/* PMU STATUS mask for 43012C0 */ +#define CY_43012_PMU_STATUS_MASK 0x1AC + +/* PMU CONTROL EXT mask for 43012C0 */ +#define CY_43012_PMU_CONTROL_EXT_MASK 0x11 + +/* PMU Watchdog Counter Tick value for 43012C0 */ +#define CY_43012_PMU_WATCHDOG_TICK_VAL 0x04 + +/* PMU Watchdog Counter Tick value for 4373 */ +#define CY_4373_PMU_WATCHDOG_TICK_VAL 0x04 + +/* Minimum PMU resource mask for 4373 */ +#define CY_4373_PMU_MIN_RES_MASK 0xFCAFF7F + +/* CYW55572 dedicated space and RAM base */ +#define CYW55572_TCAM_SIZE 0x800 +#define CYW55572_TRXHDR_SIZE 0x2b4 +#define CYW55572_RAM_BASE (0x370000 + \ + CYW55572_TCAM_SIZE + CYW55572_TRXHDR_SIZE) + +#define BRCMF_BLHS_POLL_INTERVAL 10 /* msec */ +#define BRCMF_BLHS_D2H_READY_TIMEOUT 100 /* msec */ +#define BRCMF_BLHS_D2H_TRXHDR_PARSE_DONE_TIMEOUT 50 /* msec */ +#define BRCMF_BLHS_D2H_VALDN_DONE_TIMEOUT 250 /* msec */ + +/* Bootloader handshake flags - dongle to host */ +#define BRCMF_BLHS_D2H_START BIT(0) +#define BRCMF_BLHS_D2H_READY BIT(1) +#define BRCMF_BLHS_D2H_STEADY BIT(2) +#define BRCMF_BLHS_D2H_TRXHDR_PARSE_DONE BIT(3) +#define BRCMF_BLHS_D2H_VALDN_START BIT(4) +#define BRCMF_BLHS_D2H_VALDN_RESULT BIT(5) +#define BRCMF_BLHS_D2H_VALDN_DONE BIT(6) + +/* Bootloader handshake flags - host to dongle */ +#define BRCMF_BLHS_H2D_DL_FW_START BIT(0) +#define BRCMF_BLHS_H2D_DL_FW_DONE BIT(1) +#define BRCMF_BLHS_H2D_DL_NVRAM_DONE BIT(2) +#define BRCMF_BLHS_H2D_BL_RESET_ON_ERROR BIT(3) + struct brcmf_core_priv { struct brcmf_core pub; u32 wrapbase; @@ -639,6 +683,7 @@ static void brcmf_chip_socram_ramsize(struct brcmf_core_priv *sr, u32 *ramsize, *srsize = (32 * 1024); break; case BRCM_CC_43430_CHIP_ID: + case CY_CC_43439_CHIP_ID: /* assume sr for now as we can not check * firmware sr capability at this point. */ @@ -726,6 +771,10 @@ static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci) case BRCM_CC_4364_CHIP_ID: case CY_CC_4373_CHIP_ID: return 0x160000; + case CY_CC_89459_CHIP_ID: + return ((ci->pub.chiprev < 9) ? 0x180000 : 0x160000); + case CY_CC_55572_CHIP_ID: + return CYW55572_RAM_BASE; default: brcmf_err("unknown chip: %s\n", ci->pub.name); break; @@ -744,6 +793,9 @@ int brcmf_chip_get_raminfo(struct brcmf_chip *pub) if (mem) { mem_core = container_of(mem, struct brcmf_core_priv, pub); ci->pub.ramsize = brcmf_chip_tcm_ramsize(mem_core); + if (ci->pub.chip == CY_CC_55572_CHIP_ID) + ci->pub.ramsize -= (CYW55572_TCAM_SIZE + + CYW55572_TRXHDR_SIZE); ci->pub.rambase = brcmf_chip_tcm_rambase(ci); if (!ci->pub.rambase) { brcmf_err("RAM base not provided with ARM CR4 core\n"); @@ -941,6 +993,144 @@ int brcmf_chip_dmp_erom_scan(struct brcmf_chip_priv *ci) return 0; } +static void brcmf_blhs_init(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + u32 addr; + + chip = container_of(pub, struct brcmf_chip_priv, pub); + addr = pub->blhs->h2d; + pub->blhs->write(chip->ctx, addr, 0); +} + +static int brcmf_blhs_is_bootloader_ready(struct brcmf_chip_priv *chip) +{ + u32 regdata; + u32 addr; + + addr = chip->pub.blhs->d2h; + SPINWAIT_MS((chip->pub.blhs->read(chip->ctx, addr) & + BRCMF_BLHS_D2H_READY) == 0, + BRCMF_BLHS_D2H_READY_TIMEOUT, BRCMF_BLHS_POLL_INTERVAL); + + regdata = chip->pub.blhs->read(chip->ctx, addr); + if (!(regdata & BRCMF_BLHS_D2H_READY)) { + brcmf_err("Timeout waiting for bootloader ready\n"); + return -EPERM; + } + + return 0; +} + +static int brcmf_blhs_prep_fw_download(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + u32 addr; + int err; + + /* Host indication for bootloader to start the init */ + brcmf_blhs_init(pub); + + chip = container_of(pub, struct brcmf_chip_priv, pub); + err = brcmf_blhs_is_bootloader_ready(chip); + if (err) + return err; + + /* Host notification about FW download start */ + addr = pub->blhs->h2d; + pub->blhs->write(chip->ctx, addr, BRCMF_BLHS_H2D_DL_FW_START); + + return 0; +} + +static int brcmf_blhs_post_fw_download(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + u32 addr; + u32 regdata; + + chip = container_of(pub, struct brcmf_chip_priv, pub); + addr = pub->blhs->h2d; + pub->blhs->write(chip->ctx, addr, BRCMF_BLHS_H2D_DL_FW_DONE); + addr = pub->blhs->d2h; + SPINWAIT_MS((pub->blhs->read(chip->ctx, addr) & + BRCMF_BLHS_D2H_TRXHDR_PARSE_DONE) == 0, + BRCMF_BLHS_D2H_TRXHDR_PARSE_DONE_TIMEOUT, + BRCMF_BLHS_POLL_INTERVAL); + + regdata = pub->blhs->read(chip->ctx, addr); + if (!(regdata & BRCMF_BLHS_D2H_TRXHDR_PARSE_DONE)) { + brcmf_err("TRX header parsing failed\n"); + + /* Host indication for bootloader to get reset on error */ + addr = pub->blhs->h2d; + regdata = pub->blhs->read(chip->ctx, addr); + regdata |= BRCMF_BLHS_H2D_BL_RESET_ON_ERROR; + pub->blhs->write(chip->ctx, addr, regdata); + + return -EPERM; + } + + return 0; +} + +static void brcmf_blhs_post_nvram_download(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + u32 addr; + u32 regdata; + + chip = container_of(pub, struct brcmf_chip_priv, pub); + addr = pub->blhs->h2d; + regdata = pub->blhs->read(chip->ctx, addr); + regdata |= BRCMF_BLHS_H2D_DL_NVRAM_DONE; + pub->blhs->write(chip->ctx, addr, regdata); +} + +static int brcmf_blhs_chk_validation(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + u32 addr; + u32 regdata; + + chip = container_of(pub, struct brcmf_chip_priv, pub); + addr = pub->blhs->d2h; + SPINWAIT_MS((pub->blhs->read(chip->ctx, addr) & + BRCMF_BLHS_D2H_VALDN_DONE) == 0, + BRCMF_BLHS_D2H_VALDN_DONE_TIMEOUT, + BRCMF_BLHS_POLL_INTERVAL); + + regdata = pub->blhs->read(chip->ctx, addr); + if (!(regdata & BRCMF_BLHS_D2H_VALDN_DONE) || + !(regdata & BRCMF_BLHS_D2H_VALDN_RESULT)) { + brcmf_err("TRX image validation check failed\n"); + + /* Host notification for bootloader to get reset on error */ + addr = pub->blhs->h2d; + regdata = pub->blhs->read(chip->ctx, addr); + regdata |= BRCMF_BLHS_H2D_BL_RESET_ON_ERROR; + pub->blhs->write(chip->ctx, addr, regdata); + + return -EPERM; + } + + return 0; +} + +static int brcmf_blhs_post_watchdog_reset(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + int err; + + /* Host indication for bootloader to start the init */ + brcmf_blhs_init(pub); + + chip = container_of(pub, struct brcmf_chip_priv, pub); + err = brcmf_blhs_is_bootloader_ready(chip); + + return err; +} + static int brcmf_chip_recognition(struct brcmf_chip_priv *ci) { struct brcmf_core *core; @@ -1091,6 +1281,7 @@ struct brcmf_chip *brcmf_chip_attach(void *ctx, const struct brcmf_buscore_ops *ops) { struct brcmf_chip_priv *chip; + struct brcmf_blhs *blhs; int err = 0; if (WARN_ON(!ops->read32)) @@ -1117,6 +1308,26 @@ struct brcmf_chip *brcmf_chip_attach(void *ctx, if (err < 0) goto fail; + blhs = NULL; + if (chip->ops->blhs_attach) { + err = chip->ops->blhs_attach(chip->ctx, &blhs, + BRCMF_BLHS_D2H_READY, + BRCMF_BLHS_D2H_READY_TIMEOUT, + BRCMF_BLHS_POLL_INTERVAL); + if (err < 0) + goto fail; + + if (blhs) { + blhs->init = brcmf_blhs_init; + blhs->prep_fwdl = brcmf_blhs_prep_fw_download; + blhs->post_fwdl = brcmf_blhs_post_fw_download; + blhs->post_nvramdl = brcmf_blhs_post_nvram_download; + blhs->chk_validation = brcmf_blhs_chk_validation; + blhs->post_wdreset = brcmf_blhs_post_watchdog_reset; + } + } + chip->pub.blhs = blhs; + err = brcmf_chip_recognition(chip); if (err < 0) goto fail; @@ -1143,6 +1354,7 @@ void brcmf_chip_detach(struct brcmf_chip *pub) list_del(&core->list); kfree(core); } + kfree(pub->blhs); kfree(chip); } @@ -1203,6 +1415,14 @@ struct brcmf_core *brcmf_chip_get_pmu(struct brcmf_chip *pub) return cc; } +struct brcmf_core *brcmf_chip_get_gci(struct brcmf_chip *pub) +{ + struct brcmf_core *gci; + + gci = brcmf_chip_get_core(pub, BCMA_CORE_GCI); + return gci; +} + bool brcmf_chip_iscoreup(struct brcmf_core *pub) { struct brcmf_core_priv *core; @@ -1244,7 +1464,8 @@ brcmf_chip_cm3_set_passive(struct brcmf_chip_priv *chip) brcmf_chip_resetcore(core, 0, 0, 0); /* disable bank #3 remap for this device */ - if (chip->pub.chip == BRCM_CC_43430_CHIP_ID) { + if (chip->pub.chip == BRCM_CC_43430_CHIP_ID || + chip->pub.chip == CY_CC_43439_CHIP_ID) { sr = container_of(core, struct brcmf_core_priv, pub); brcmf_chip_core_write32(sr, SOCRAMREGOFFS(bankidx), 3); brcmf_chip_core_write32(sr, SOCRAMREGOFFS(bankpda), 0); @@ -1274,7 +1495,8 @@ brcmf_chip_cr4_set_passive(struct brcmf_chip_priv *chip) { struct brcmf_core *core; - brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CR4); + if (!chip->pub.blhs) + brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CR4); core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211); brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET | @@ -1401,10 +1623,13 @@ bool brcmf_chip_sr_capable(struct brcmf_chip *pub) reg = chip->ops->read32(chip->ctx, addr); return (reg & pmu_cc3_mask) != 0; case BRCM_CC_43430_CHIP_ID: + case CY_CC_43439_CHIP_ID: addr = CORE_CC_REG(base, sr_control1); reg = chip->ops->read32(chip->ctx, addr); return reg != 0; case CY_CC_4373_CHIP_ID: + case CY_CC_55572_CHIP_ID: + case CY_CC_89459_CHIP_ID: /* explicitly check SR engine enable bit */ addr = CORE_CC_REG(base, sr_control0); reg = chip->ops->read32(chip->ctx, addr); @@ -1427,3 +1652,150 @@ bool brcmf_chip_sr_capable(struct brcmf_chip *pub) PMU_RCTL_LOGIC_DISABLE_MASK)) == 0; } } + +void brcmf_chip_reset_pmu_regs(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + u32 addr; + u32 base; + + brcmf_dbg(TRACE, "Enter\n"); + + chip = container_of(pub, struct brcmf_chip_priv, pub); + base = brcmf_chip_get_pmu(pub)->base; + + switch (pub->chip) { + case CY_CC_43012_CHIP_ID: + /* SW scratch */ + addr = CORE_CC_REG(base, swscratch); + chip->ops->write32(chip->ctx, addr, 0); + + /* PMU status */ + addr = CORE_CC_REG(base, pmustatus); + chip->ops->write32(chip->ctx, addr, + CY_43012_PMU_STATUS_MASK); + + /* PMU control ext */ + addr = CORE_CC_REG(base, pmucontrol_ext); + chip->ops->write32(chip->ctx, addr, + CY_43012_PMU_CONTROL_EXT_MASK); + break; + + default: + brcmf_err("Unsupported chip id\n"); + break; + } +} + +void brcmf_chip_set_default_min_res_mask(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + u32 addr; + u32 base; + + brcmf_dbg(TRACE, "Enter\n"); + + chip = container_of(pub, struct brcmf_chip_priv, pub); + base = brcmf_chip_get_pmu(pub)->base; + switch (pub->chip) { + case CY_CC_43012_CHIP_ID: + addr = CORE_CC_REG(base, min_res_mask); + chip->ops->write32(chip->ctx, addr, + CY_43012_PMU_MIN_RES_MASK); + break; + + default: + brcmf_err("Unsupported chip id\n"); + break; + } +} + +void brcmf_chip_ulp_reset_lhl_regs(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + u32 base; + u32 addr; + + brcmf_dbg(TRACE, "Enter\n"); + + chip = container_of(pub, struct brcmf_chip_priv, pub); + base = brcmf_chip_get_gci(pub)->base; + + /* LHL Top Level Power Sequence Control */ + addr = CORE_GCI_REG(base, lhl_top_pwrseq_ctl_adr); + chip->ops->write32(chip->ctx, addr, 0); + + /* GPIO Interrupt Enable0 */ + addr = CORE_GCI_REG(base, gpio_int_en_port_adr[0]); + chip->ops->write32(chip->ctx, addr, 0); + + /* GPIO Interrupt Status0 */ + addr = CORE_GCI_REG(base, gpio_int_st_port_adr[0]); + chip->ops->write32(chip->ctx, addr, ~0); + + /* WL ARM Timer0 Interrupt Mask */ + addr = CORE_GCI_REG(base, lhl_wl_armtim0_intrp_adr); + chip->ops->write32(chip->ctx, addr, 0); + + /* WL ARM Timer0 Interrupt Status */ + addr = CORE_GCI_REG(base, lhl_wl_armtim0_st_adr); + chip->ops->write32(chip->ctx, addr, ~0); + + /* WL ARM Timer */ + addr = CORE_GCI_REG(base, lhl_wl_armtim0_adr); + chip->ops->write32(chip->ctx, addr, 0); + + /* WL MAC Timer0 Interrupt Mask */ + addr = CORE_GCI_REG(base, lhl_wl_mactim0_intrp_adr); + chip->ops->write32(chip->ctx, addr, 0); + + /* WL MAC Timer0 Interrupt Status */ + addr = CORE_GCI_REG(base, lhl_wl_mactim0_st_adr); + chip->ops->write32(chip->ctx, addr, ~0); + + /* WL MAC TimerInt0 */ + addr = CORE_GCI_REG(base, lhl_wl_mactim_int0_adr); + chip->ops->write32(chip->ctx, addr, 0x0); +} + +void brcmf_chip_reset_watchdog(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + u32 base; + u32 addr; + + brcmf_dbg(TRACE, "Enter\n"); + + chip = container_of(pub, struct brcmf_chip_priv, pub); + base = brcmf_chip_get_pmu(pub)->base; + + switch (pub->chip) { + case CY_CC_43012_CHIP_ID: + addr = CORE_CC_REG(base, min_res_mask); + chip->ops->write32(chip->ctx, addr, + CY_43012_PMU_MIN_RES_MASK); + /* Watchdog res mask */ + addr = CORE_CC_REG(base, watchdog_res_mask); + chip->ops->write32(chip->ctx, addr, + CY_43012_PMU_MIN_RES_MASK); + /* PMU watchdog */ + addr = CORE_CC_REG(base, pmuwatchdog); + chip->ops->write32(chip->ctx, addr, + CY_43012_PMU_WATCHDOG_TICK_VAL); + break; + case CY_CC_4373_CHIP_ID: + addr = CORE_CC_REG(base, min_res_mask); + chip->ops->write32(chip->ctx, addr, + CY_4373_PMU_MIN_RES_MASK); + addr = CORE_CC_REG(base, watchdog_res_mask); + chip->ops->write32(chip->ctx, addr, + CY_4373_PMU_MIN_RES_MASK); + addr = CORE_CC_REG(base, pmuwatchdog); + chip->ops->write32(chip->ctx, addr, + CY_4373_PMU_WATCHDOG_TICK_VAL); + mdelay(100); + break; + default: + break; + } +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h index 8fa38658e727a..c1a803323f8d0 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h @@ -8,7 +8,12 @@ #include #define CORE_CC_REG(base, field) \ - (base + offsetof(struct chipcregs, field)) + ((base) + offsetof(struct chipcregs, field)) + +#define CORE_GCI_REG(base, field) \ + ((base) + offsetof(struct chipgciregs, field)) + +struct brcmf_blhs; /** * struct brcmf_chip - chip level information. @@ -23,6 +28,7 @@ * @ramsize: amount of RAM on chip including retention. * @srsize: amount of retention RAM on chip. * @name: string representation of the chip identifier. + * @blhs: bootlooder handshake handle. */ struct brcmf_chip { u32 chip; @@ -35,6 +41,7 @@ struct brcmf_chip { u32 ramsize; u32 srsize; char name[12]; + struct brcmf_blhs *blhs; }; /** @@ -59,6 +66,7 @@ struct brcmf_core { * @setup: bus-specific core setup. * @active: chip becomes active. * The callback should use the provided @rstvec when non-zero. + * @blhs_attach: attach bootloader handshake handle */ struct brcmf_buscore_ops { u32 (*read32)(void *ctx, u32 addr); @@ -67,6 +75,35 @@ struct brcmf_buscore_ops { int (*reset)(void *ctx, struct brcmf_chip *chip); int (*setup)(void *ctx, struct brcmf_chip *chip); void (*activate)(void *ctx, struct brcmf_chip *chip, u32 rstvec); + int (*blhs_attach)(void *ctx, struct brcmf_blhs **blhs, u32 flag, + uint timeout, uint interval); +}; + +/** + * struct brcmf_blhs - bootloader handshake handle related information. + * + * @d2h: offset of dongle to host register for the handshake. + * @h2d: offset of host to dongle register for the handshake. + * @init: bootloader handshake initialization. + * @prep_fwdl: handshake before firmware download. + * @post_fwdl: handshake after firmware download. + * @post_nvramdl: handshake after nvram download. + * @chk_validation: handshake for firmware validation check. + * @post_wdreset: handshake after watchdog reset. + * @read: read value with register offset for the handshake. + * @write: write value with register offset for the handshake. + */ +struct brcmf_blhs { + u32 d2h; + u32 h2d; + void (*init)(struct brcmf_chip *pub); + int (*prep_fwdl)(struct brcmf_chip *pub); + int (*post_fwdl)(struct brcmf_chip *pub); + void (*post_nvramdl)(struct brcmf_chip *pub); + int (*chk_validation)(struct brcmf_chip *pub); + int (*post_wdreset)(struct brcmf_chip *pub); + u32 (*read)(void *ctx, u32 addr); + void (*write)(void *ctx, u32 addr, u32 value); }; int brcmf_chip_get_raminfo(struct brcmf_chip *pub); @@ -85,5 +122,9 @@ void brcmf_chip_set_passive(struct brcmf_chip *ci); bool brcmf_chip_set_active(struct brcmf_chip *ci, u32 rstvec); bool brcmf_chip_sr_capable(struct brcmf_chip *pub); char *brcmf_chip_name(u32 chipid, u32 chiprev, char *buf, uint len); +void brcmf_chip_reset_watchdog(struct brcmf_chip *pub); +void brcmf_chip_ulp_reset_lhl_regs(struct brcmf_chip *pub); +void brcmf_chip_reset_pmu_regs(struct brcmf_chip *pub); +void brcmf_chip_set_default_min_res_mask(struct brcmf_chip *pub); #endif /* BRCMF_AXIDMP_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c index e3758bd86acf0..5b132e90d9afa 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c @@ -20,6 +20,12 @@ #include "of.h" #include "firmware.h" #include "chip.h" +#include "defs.h" +#include "fweh.h" +#include +#include +#include +#include "pcie.h" MODULE_AUTHOR("Broadcom Corporation"); MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver."); @@ -67,6 +73,14 @@ static int brcmf_iapp_enable; module_param_named(iapp, brcmf_iapp_enable, int, 0); MODULE_PARM_DESC(iapp, "Enable partial support for the obsoleted Inter-Access Point Protocol"); +static int brcmf_eap_restrict; +module_param_named(eap_restrict, brcmf_eap_restrict, int, 0400); +MODULE_PARM_DESC(eap_restrict, "Block non-802.1X frames until auth finished"); + +static int brcmf_max_pm; +module_param_named(max_pm, brcmf_max_pm, int, 0); +MODULE_PARM_DESC(max_pm, "Use max power management mode by default"); + #ifdef DEBUG /* always succeed brcmf_bus_started() */ static int brcmf_ignore_probe_fail; @@ -74,9 +88,19 @@ module_param_named(ignore_probe_fail, brcmf_ignore_probe_fail, int, 0); MODULE_PARM_DESC(ignore_probe_fail, "always succeed probe for debugging"); #endif +static int brcmf_fw_ap_select; +module_param_named(fw_ap_select, brcmf_fw_ap_select, int, 0400); +MODULE_PARM_DESC(fw_ap_select, "Allow FW for AP selection"); + static struct brcmfmac_platform_data *brcmfmac_pdata; struct brcmf_mp_global_t brcmf_mp_global; +static int brcmf_reboot_callback(struct notifier_block *this, unsigned long code, void *unused); +static struct notifier_block brcmf_reboot_notifier = { + .notifier_call = brcmf_reboot_callback, + .priority = 1, +}; + void brcmf_c_set_joinpref_default(struct brcmf_if *ifp) { struct brcmf_pub *drvr = ifp->drvr; @@ -201,8 +225,10 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) char *clmver; char *ptr; s32 err; + struct eventmsgs_ext *eventmask_msg = NULL; + u8 msglen; - /* retreive mac address */ + /* retrieve mac addresses */ err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr, sizeof(ifp->mac_addr)); if (err < 0) { @@ -292,6 +318,11 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) brcmf_dbg(INFO, "CLM version = %s\n", clmver); } + /* set apsta */ + err = brcmf_fil_iovar_int_set(ifp, "apsta", 1); + if (err) + brcmf_info("failed setting apsta, %d\n", err); + /* set mpc */ err = brcmf_fil_iovar_int_set(ifp, "mpc", 1); if (err) { @@ -316,6 +347,43 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) goto done; } + /* Enable event_msg_ext specific to 43012 chip */ + if (bus->chip == CY_CC_43012_CHIP_ID) { + /* Program event_msg_ext to support event larger than 128 */ + msglen = (roundup(BRCMF_E_LAST, NBBY) / NBBY) + + EVENTMSGS_EXT_STRUCT_SIZE; + /* Allocate buffer for eventmask_msg */ + eventmask_msg = kzalloc(msglen, GFP_KERNEL); + if (!eventmask_msg) { + err = -ENOMEM; + goto done; + } + + /* Read the current programmed event_msgs_ext */ + eventmask_msg->ver = EVENTMSGS_VER; + eventmask_msg->len = roundup(BRCMF_E_LAST, NBBY) / NBBY; + err = brcmf_fil_iovar_data_get(ifp, "event_msgs_ext", + eventmask_msg, + msglen); + + /* Enable ULP event */ + brcmf_dbg(EVENT, "enable event ULP\n"); + setbit(eventmask_msg->mask, BRCMF_E_ULP); + + /* Write updated Event mask */ + eventmask_msg->ver = EVENTMSGS_VER; + eventmask_msg->command = EVENTMSGS_SET_MASK; + eventmask_msg->len = (roundup(BRCMF_E_LAST, NBBY) / NBBY); + + err = brcmf_fil_iovar_data_set(ifp, "event_msgs_ext", + eventmask_msg, msglen); + if (err) { + brcmf_err("Set event_msgs_ext error (%d)\n", err); + kfree(eventmask_msg); + goto done; + } + kfree(eventmask_msg); + } /* Setup default scan channel time */ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME, BRCMF_DEFAULT_SCAN_CHANNEL_TIME); @@ -336,6 +404,20 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) /* Enable tx beamforming, errors can be ignored (not supported) */ (void)brcmf_fil_iovar_int_set(ifp, "txbf", 1); + err = brcmf_fil_iovar_int_set(ifp, "chanspec", 0x1001); + if (err < 0) + bphy_err(drvr, "Initial Channel failed %d\n", err); + /* add unicast packet filter */ + err = brcmf_pktfilter_add_remove(ifp->ndev, + BRCMF_UNICAST_FILTER_NUM, true); + if (err == -BRCMF_FW_UNSUPPORTED) { + /* FW not support can be ignored */ + err = 0; + goto done; + } else if (err) { + bphy_err(drvr, "Add unicast filter error (%d)\n", err); + } + done: return err; } @@ -407,15 +489,18 @@ struct brcmf_mp_device *brcmf_get_module_param(struct device *dev, if (!settings) return NULL; - /* start by using the module paramaters */ + /* start by using the module parameters */ settings->p2p_enable = !!brcmf_p2p_enable; settings->feature_disable = brcmf_feature_disable; settings->fcmode = brcmf_fcmode; settings->roamoff = !!brcmf_roamoff; settings->iapp = !!brcmf_iapp_enable; + settings->eap_restrict = !!brcmf_eap_restrict; + settings->default_pm = !!brcmf_max_pm ? PM_MAX : PM_FAST; #ifdef DEBUG settings->ignore_probe_fail = !!brcmf_ignore_probe_fail; #endif + settings->fw_ap_select = !!brcmf_fw_ap_select; if (bus_type == BRCMF_BUSTYPE_SDIO) settings->bus.sdio.txglomsz = brcmf_sdiod_txglomsz; @@ -454,13 +539,22 @@ void brcmf_release_module_param(struct brcmf_mp_device *module_param) kfree(module_param); } +static int +brcmf_reboot_callback(struct notifier_block *this, unsigned long code, void *unused) +{ + brcmf_dbg(INFO, "code = %ld\n", code); + if (code == SYS_RESTART) + brcmf_core_exit(); + return NOTIFY_DONE; +} + static int __init brcmf_common_pd_probe(struct platform_device *pdev) { brcmf_dbg(INFO, "Enter\n"); brcmfmac_pdata = dev_get_platdata(&pdev->dev); - if (brcmfmac_pdata->power_on) + if (brcmfmac_pdata && brcmfmac_pdata->power_on) brcmfmac_pdata->power_on(); return 0; @@ -492,7 +586,7 @@ static int __init brcmfmac_module_init(void) if (err == -ENODEV) brcmf_dbg(INFO, "No platform data available.\n"); - /* Initialize global module paramaters */ + /* Initialize global module parameters */ brcmf_mp_attach(); /* Continue the initialization by registering the different busses */ @@ -500,6 +594,8 @@ static int __init brcmfmac_module_init(void) if (err) { if (brcmfmac_pdata) platform_driver_unregister(&brcmf_pd); + } else { + register_reboot_notifier(&brcmf_reboot_notifier); } return err; @@ -508,6 +604,7 @@ static int __init brcmfmac_module_init(void) static void __exit brcmfmac_module_exit(void) { brcmf_core_exit(); + unregister_reboot_notifier(&brcmf_reboot_notifier); if (brcmfmac_pdata) platform_driver_unregister(&brcmf_pd); } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h index 8b5f49997c8b5..d9f740a3e4e7e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h @@ -37,7 +37,10 @@ extern struct brcmf_mp_global_t brcmf_mp_global; * @feature_disable: Feature_disable bitmask. * @fcmode: FWS flow control. * @roamoff: Firmware roaming off? + * @eap_restrict: Not allow data tx/rx until 802.1X auth succeeds + * @default_pm: default power management (PM) mode. * @ignore_probe_fail: Ignore probe failure. + * @fw_ap_select: Allow FW to select AP. * @country_codes: If available, pointer to struct for translating country codes * @bus: Bus specific platform data. Only SDIO at the mmoment. */ @@ -47,7 +50,10 @@ struct brcmf_mp_device { int fcmode; bool roamoff; bool iapp; + bool eap_restrict; + int default_pm; bool ignore_probe_fail; + bool fw_ap_select; struct brcmfmac_pd_cc *country_codes; const char *board_type; union { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 3dd28f5fef19e..b859715402661 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -62,6 +63,14 @@ struct wlc_d11rxhdr { s8 rxpwr[4]; } __packed; +#define BRCMF_IF_STA_LIST_LOCK_INIT(ifp) spin_lock_init(&(ifp)->sta_list_lock) +#define BRCMF_IF_STA_LIST_LOCK(ifp, flags) \ + spin_lock_irqsave(&(ifp)->sta_list_lock, (flags)) +#define BRCMF_IF_STA_LIST_UNLOCK(ifp, flags) \ + spin_unlock_irqrestore(&(ifp)->sta_list_lock, (flags)) + +#define BRCMF_STA_NULL ((struct brcmf_sta *)NULL) + char *brcmf_ifname(struct brcmf_if *ifp) { if (!ifp) @@ -96,6 +105,11 @@ void brcmf_configure_arp_nd_offload(struct brcmf_if *ifp, bool enable) s32 err; u32 mode; + if (enable && brcmf_is_apmode_operating(ifp->drvr->wiphy)) { + brcmf_dbg(TRACE, "Skip ARP/ND offload enable when soft AP is running\n"); + return; + } + if (enable) mode = BRCMF_ARP_OL_AGENT | BRCMF_ARP_OL_PEER_AUTO_REPLY; else @@ -576,6 +590,7 @@ static void brcmf_ethtool_get_drvinfo(struct net_device *ndev, static const struct ethtool_ops brcmf_ethtool_ops = { .get_drvinfo = brcmf_ethtool_get_drvinfo, + .get_ts_info = ethtool_op_get_ts_info, }; static int brcmf_netdev_stop(struct net_device *ndev) @@ -898,7 +913,9 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bsscfgidx, s32 ifidx, init_waitqueue_head(&ifp->pend_8021x_wait); spin_lock_init(&ifp->netif_stop_lock); - + BRCMF_IF_STA_LIST_LOCK_INIT(ifp); + /* Initialize STA info list */ + INIT_LIST_HEAD(&ifp->sta_list); if (mac_addr != NULL) memcpy(ifp->mac_addr, mac_addr, ETH_ALEN); @@ -1130,6 +1147,15 @@ static int brcmf_inet6addr_changed(struct notifier_block *nb, } #endif + +int brcmf_fwlog_attach(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + + return brcmf_debug_fwlog_init(drvr); +} + static int brcmf_revinfo_read(struct seq_file *s, void *data) { struct brcmf_bus *bus_if = dev_get_drvdata(s->private); @@ -1319,7 +1345,7 @@ int brcmf_alloc(struct device *dev, struct brcmf_mp_device *settings) return 0; } -int brcmf_attach(struct device *dev) +int brcmf_attach(struct device *dev, bool start_bus) { struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; @@ -1336,6 +1362,7 @@ int brcmf_attach(struct device *dev) /* Link to bus module */ drvr->hdrlen = 0; + drvr->req_mpc = 1; /* Attach and link in the protocol */ ret = brcmf_proto_attach(drvr); if (ret != 0) { @@ -1350,10 +1377,13 @@ int brcmf_attach(struct device *dev) /* attach firmware event handler */ brcmf_fweh_attach(drvr); - ret = brcmf_bus_started(drvr, drvr->ops); - if (ret != 0) { - bphy_err(drvr, "dongle is not responding: err=%d\n", ret); - goto fail; + if (start_bus) { + ret = brcmf_bus_started(drvr, drvr->ops); + if (ret != 0) { + bphy_err(drvr, "dongle is not responding: err=%d\n", + ret); + goto fail; + } } return 0; @@ -1403,7 +1433,8 @@ void brcmf_fw_crashed(struct device *dev) brcmf_dev_coredump(dev); - schedule_work(&drvr->bus_reset); + if (drvr->bus_reset.func) + schedule_work(&drvr->bus_reset); } void brcmf_detach(struct device *dev) @@ -1485,8 +1516,10 @@ int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp) !brcmf_get_pend_8021x_cnt(ifp), MAX_WAIT_FOR_8021X_TX); - if (!err) + if (!err) { bphy_err(drvr, "Timed out waiting for no pending 802.1x packets\n"); + atomic_set(&ifp->pend_8021x_cnt, 0); + } return !err; } @@ -1540,7 +1573,7 @@ int __init brcmf_core_init(void) return 0; } -void __exit brcmf_core_exit(void) +void brcmf_core_exit(void) { cancel_work_sync(&brcmf_driver_work); @@ -1555,3 +1588,270 @@ void __exit brcmf_core_exit(void) #endif } +int +brcmf_pktfilter_add_remove(struct net_device *ndev, int filter_num, bool add) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_pub *drvr = ifp->drvr; + struct brcmf_pkt_filter_le *pkt_filter; + int filter_fixed_len = offsetof(struct brcmf_pkt_filter_le, u); + int pattern_fixed_len = offsetof(struct brcmf_pkt_filter_pattern_le, + mask_and_pattern); + u16 mask_and_pattern[MAX_PKTFILTER_PATTERN_SIZE]; + int buflen = 0; + int ret = 0; + + brcmf_dbg(INFO, "%s packet filter number %d\n", + (add ? "add" : "remove"), filter_num); + + pkt_filter = kzalloc(sizeof(*pkt_filter) + + (MAX_PKTFILTER_PATTERN_SIZE * 2), GFP_ATOMIC); + if (!pkt_filter) + return -ENOMEM; + + switch (filter_num) { + case BRCMF_UNICAST_FILTER_NUM: + pkt_filter->id = 100; + pkt_filter->type = 0; + pkt_filter->negate_match = 0; + pkt_filter->u.pattern.offset = 0; + pkt_filter->u.pattern.size_bytes = 1; + mask_and_pattern[0] = 0x0001; + break; + case BRCMF_BROADCAST_FILTER_NUM: + //filter_pattern = "101 0 0 0 0xFFFFFFFFFFFF 0xFFFFFFFFFFFF"; + pkt_filter->id = 101; + pkt_filter->type = 0; + pkt_filter->negate_match = 0; + pkt_filter->u.pattern.offset = 0; + pkt_filter->u.pattern.size_bytes = 6; + mask_and_pattern[0] = 0xFFFF; + mask_and_pattern[1] = 0xFFFF; + mask_and_pattern[2] = 0xFFFF; + mask_and_pattern[3] = 0xFFFF; + mask_and_pattern[4] = 0xFFFF; + mask_and_pattern[5] = 0xFFFF; + break; + case BRCMF_MULTICAST4_FILTER_NUM: + //filter_pattern = "102 0 0 0 0xFFFFFF 0x01005E"; + pkt_filter->id = 102; + pkt_filter->type = 0; + pkt_filter->negate_match = 0; + pkt_filter->u.pattern.offset = 0; + pkt_filter->u.pattern.size_bytes = 3; + mask_and_pattern[0] = 0xFFFF; + mask_and_pattern[1] = 0x01FF; + mask_and_pattern[2] = 0x5E00; + break; + case BRCMF_MULTICAST6_FILTER_NUM: + //filter_pattern = "103 0 0 0 0xFFFF 0x3333"; + pkt_filter->id = 103; + pkt_filter->type = 0; + pkt_filter->negate_match = 0; + pkt_filter->u.pattern.offset = 0; + pkt_filter->u.pattern.size_bytes = 2; + mask_and_pattern[0] = 0xFFFF; + mask_and_pattern[1] = 0x3333; + break; + case BRCMF_MDNS_FILTER_NUM: + //filter_pattern = "104 0 0 0 0xFFFFFFFFFFFF 0x01005E0000FB"; + pkt_filter->id = 104; + pkt_filter->type = 0; + pkt_filter->negate_match = 0; + pkt_filter->u.pattern.offset = 0; + pkt_filter->u.pattern.size_bytes = 6; + mask_and_pattern[0] = 0xFFFF; + mask_and_pattern[1] = 0xFFFF; + mask_and_pattern[2] = 0xFFFF; + mask_and_pattern[3] = 0x0001; + mask_and_pattern[4] = 0x005E; + mask_and_pattern[5] = 0xFB00; + break; + case BRCMF_ARP_FILTER_NUM: + //filter_pattern = "105 0 0 12 0xFFFF 0x0806"; + pkt_filter->id = 105; + pkt_filter->type = 0; + pkt_filter->negate_match = 0; + pkt_filter->u.pattern.offset = 12; + pkt_filter->u.pattern.size_bytes = 2; + mask_and_pattern[0] = 0xFFFF; + mask_and_pattern[1] = 0x0608; + break; + case BRCMF_BROADCAST_ARP_FILTER_NUM: + //filter_pattern = "106 0 0 0 + //0xFFFFFFFFFFFF0000000000000806 + //0xFFFFFFFFFFFF0000000000000806"; + pkt_filter->id = 106; + pkt_filter->type = 0; + pkt_filter->negate_match = 0; + pkt_filter->u.pattern.offset = 0; + pkt_filter->u.pattern.size_bytes = 14; + mask_and_pattern[0] = 0xFFFF; + mask_and_pattern[1] = 0xFFFF; + mask_and_pattern[2] = 0xFFFF; + mask_and_pattern[3] = 0x0000; + mask_and_pattern[4] = 0x0000; + mask_and_pattern[5] = 0x0000; + mask_and_pattern[6] = 0x0608; + mask_and_pattern[7] = 0xFFFF; + mask_and_pattern[8] = 0xFFFF; + mask_and_pattern[9] = 0xFFFF; + mask_and_pattern[10] = 0x0000; + mask_and_pattern[11] = 0x0000; + mask_and_pattern[12] = 0x0000; + mask_and_pattern[13] = 0x0608; + break; + default: + ret = -EINVAL; + goto failed; + } + memcpy(pkt_filter->u.pattern.mask_and_pattern, mask_and_pattern, + pkt_filter->u.pattern.size_bytes * 2); + buflen = filter_fixed_len + pattern_fixed_len + + pkt_filter->u.pattern.size_bytes * 2; + + if (add) { + /* Add filter */ + ifp->fwil_fwerr = true; + ret = brcmf_fil_iovar_data_set(ifp, "pkt_filter_add", + pkt_filter, buflen); + ifp->fwil_fwerr = false; + if (ret) + goto failed; + drvr->pkt_filter[filter_num].id = pkt_filter->id; + drvr->pkt_filter[filter_num].enable = 0; + + } else { + /* Delete filter */ + ifp->fwil_fwerr = true; + ret = brcmf_fil_iovar_int_set(ifp, "pkt_filter_delete", + pkt_filter->id); + ifp->fwil_fwerr = false; + if (ret == -BRCMF_FW_BADARG) + ret = 0; + if (ret) + goto failed; + + drvr->pkt_filter[filter_num].id = 0; + drvr->pkt_filter[filter_num].enable = 0; + } +failed: + if (ret) + brcmf_err("%s packet filter failed, ret=%d\n", + (add ? "add" : "remove"), ret); + + kfree(pkt_filter); + return ret; +} + +int brcmf_pktfilter_enable(struct net_device *ndev, bool enable) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_pub *drvr = ifp->drvr; + int ret = 0; + int idx = 0; + + for (idx = 0; idx < MAX_PKT_FILTER_COUNT; ++idx) { + if (drvr->pkt_filter[idx].id != 0) { + drvr->pkt_filter[idx].enable = enable; + ret = brcmf_fil_iovar_data_set(ifp, "pkt_filter_enable", + &drvr->pkt_filter[idx], + sizeof(struct brcmf_pkt_filter_enable_le)); + if (ret) { + brcmf_err("%s packet filter id(%d) failed, ret=%d\n", + (enable ? "enable" : "disable"), + drvr->pkt_filter[idx].id, ret); + } + } + } + return ret; +} + +/** Find STA with MAC address ea in an interface's STA list. */ +struct brcmf_sta * +brcmf_find_sta(struct brcmf_if *ifp, const u8 *ea) +{ + struct brcmf_sta *sta; + unsigned long flags; + + BRCMF_IF_STA_LIST_LOCK(ifp, flags); + list_for_each_entry(sta, &ifp->sta_list, list) { + if (!memcmp(sta->ea.octet, ea, ETH_ALEN)) { + brcmf_dbg(INFO, "Found STA: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x into sta list\n", + sta->ea.octet[0], sta->ea.octet[1], + sta->ea.octet[2], sta->ea.octet[3], + sta->ea.octet[4], sta->ea.octet[5]); + BRCMF_IF_STA_LIST_UNLOCK(ifp, flags); + return sta; + } + } + BRCMF_IF_STA_LIST_UNLOCK(ifp, flags); + + return BRCMF_STA_NULL; +} + +/** Add STA into the interface's STA list. */ +struct brcmf_sta * +brcmf_add_sta(struct brcmf_if *ifp, const u8 *ea) +{ + struct brcmf_sta *sta; + unsigned long flags; + + sta = kzalloc(sizeof(*sta), GFP_KERNEL); + if (sta == BRCMF_STA_NULL) { + brcmf_err("Alloc failed\n"); + return BRCMF_STA_NULL; + } + memcpy(sta->ea.octet, ea, ETH_ALEN); + brcmf_dbg(INFO, "Add STA: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x into sta list\n", + sta->ea.octet[0], sta->ea.octet[1], + sta->ea.octet[2], sta->ea.octet[3], + sta->ea.octet[4], sta->ea.octet[5]); + + /* link the sta and the dhd interface */ + sta->ifp = ifp; + INIT_LIST_HEAD(&sta->list); + + BRCMF_IF_STA_LIST_LOCK(ifp, flags); + + list_add_tail(&sta->list, &ifp->sta_list); + + BRCMF_IF_STA_LIST_UNLOCK(ifp, flags); + return sta; +} + +/** Delete STA from the interface's STA list. */ +void +brcmf_del_sta(struct brcmf_if *ifp, const u8 *ea) +{ + struct brcmf_sta *sta, *next; + unsigned long flags; + + BRCMF_IF_STA_LIST_LOCK(ifp, flags); + list_for_each_entry_safe(sta, next, &ifp->sta_list, list) { + if (!memcmp(sta->ea.octet, ea, ETH_ALEN)) { + brcmf_dbg(INFO, "del STA: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x from sta list\n", + ea[0], ea[1], ea[2], ea[3], + ea[4], ea[5]); + list_del(&sta->list); + kfree(sta); + } + } + + BRCMF_IF_STA_LIST_UNLOCK(ifp, flags); +} + +/** Add STA if it doesn't exist. Not reentrant. */ +struct brcmf_sta* +brcmf_findadd_sta(struct brcmf_if *ifp, const u8 *ea) +{ + struct brcmf_sta *sta = NULL; + + sta = brcmf_find_sta(ifp, ea); + + if (!sta) { + /* Add entry */ + sta = brcmf_add_sta(ifp, ea); + } + return sta; +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h index 5767d665cee50..8a86d3927cedd 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h @@ -12,6 +12,7 @@ #include #include "fweh.h" +#include "fwil_types.h" #define TOE_TX_CSUM_OL 0x00000001 #define TOE_RX_CSUM_OL 0x00000002 @@ -123,6 +124,7 @@ struct brcmf_pub { u32 feat_flags; u32 chip_quirks; + int req_mpc; struct brcmf_rev_info revinfo; #ifdef DEBUG @@ -136,6 +138,9 @@ struct brcmf_pub { struct work_struct bus_reset; u8 clmver[BRCMF_DCMD_SMLEN]; + struct brcmf_pkt_filter_enable_le pkt_filter[MAX_PKT_FILTER_COUNT]; + u8 sta_mac_idx; + }; /* forward declarations */ @@ -185,6 +190,7 @@ struct brcmf_if { struct brcmf_fws_mac_descriptor *fws_desc; int ifidx; s32 bsscfgidx; + bool isap; u8 mac_addr[ETH_ALEN]; u8 netif_stop; spinlock_t netif_stop_lock; @@ -193,6 +199,20 @@ struct brcmf_if { struct in6_addr ipv6_addr_tbl[NDOL_MAX_ENTRIES]; u8 ipv6addr_idx; bool fwil_fwerr; + struct list_head sta_list; /* sll of associated stations */ + spinlock_t sta_list_lock; + bool fmac_pkt_fwd_en; +}; + +struct ether_addr { + u8 octet[ETH_ALEN]; +}; + +/** Per STA params. A list of dhd_sta objects are managed in dhd_if */ +struct brcmf_sta { + void *ifp; /* associated brcm_if */ + struct ether_addr ea; /* stations ethernet mac address */ + struct list_head list; /* link into brcmf_if::sta_list */ }; int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp); @@ -214,6 +234,11 @@ void brcmf_net_detach(struct net_device *ndev, bool rtnl_locked); int brcmf_net_mon_attach(struct brcmf_if *ifp); void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on); int __init brcmf_core_init(void); -void __exit brcmf_core_exit(void); - +void brcmf_core_exit(void); +int brcmf_pktfilter_add_remove(struct net_device *ndev, int filter_num, + bool add); +int brcmf_pktfilter_enable(struct net_device *ndev, bool enable); +void brcmf_del_sta(struct brcmf_if *ifp, const u8 *ea); +struct brcmf_sta *brcmf_find_sta(struct brcmf_if *ifp, const u8 *ea); +struct brcmf_sta *brcmf_findadd_sta(struct brcmf_if *ifp, const u8 *ea); #endif /* BRCMFMAC_CORE_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c index eecf8a38d94aa..f5da560bfc124 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c @@ -14,6 +14,82 @@ #include "fweh.h" #include "debug.h" +static int +brcmf_debug_msgtrace_seqchk(u32 *prev, u32 cur) +{ + if ((cur == 0 && *prev == 0xFFFFFFFF) || ((cur - *prev) == 1)) { + goto done; + } else if (cur == *prev) { + brcmf_dbg(FWCON, "duplicate trace\n"); + return -1; + } else if (cur > *prev) { + brcmf_dbg(FWCON, "lost %d packets\n", cur - *prev); + } else { + brcmf_dbg(FWCON, "seq out of order, host %d, dongle %d\n", + *prev, cur); + } +done: + *prev = cur; + return 0; +} + +static int +brcmf_debug_msg_parser(void *event_data) +{ + int err = 0; + struct msgtrace_hdr *hdr; + char *data, *s; + static u32 seqnum_prev; + + hdr = (struct msgtrace_hdr *)event_data; + data = (char *)event_data + MSGTRACE_HDRLEN; + + /* There are 2 bytes available at the end of data */ + data[ntohs(hdr->len)] = '\0'; + + if (ntohl(hdr->discarded_bytes) || ntohl(hdr->discarded_printf)) { + brcmf_dbg(FWCON, "Discarded_bytes %d discarded_printf %d\n", + ntohl(hdr->discarded_bytes), + ntohl(hdr->discarded_printf)); + } + + err = brcmf_debug_msgtrace_seqchk(&seqnum_prev, ntohl(hdr->seqnum)); + if (err) + return err; + + while (*data != '\0' && (s = strstr(data, "\n")) != NULL) { + *s = '\0'; + brcmf_dbg(FWCON, "CONSOLE: %s\n", data); + data = s + 1; + } + if (*data) + brcmf_dbg(FWCON, "CONSOLE: %s", data); + + return err; +} + +static int +brcmf_debug_trace_parser(struct brcmf_if *ifp, + const struct brcmf_event_msg *evtmsg, + void *event_data) +{ + int err = 0; + struct msgtrace_hdr *hdr; + + hdr = (struct msgtrace_hdr *)event_data; + if (hdr->version != MSGTRACE_VERSION) { + brcmf_dbg(FWCON, "trace version mismatch host %d dngl %d\n", + MSGTRACE_VERSION, hdr->version); + err = -EPROTO; + return err; + } + + if (hdr->trace_type == MSGTRACE_HDR_TYPE_MSG) + err = brcmf_debug_msg_parser(event_data); + + return err; +} + int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data, size_t len) { @@ -42,6 +118,13 @@ int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data, return 0; } + +int brcmf_debug_fwlog_init(struct brcmf_pub *drvr) +{ + return brcmf_fweh_register(drvr, BRCMF_E_TRACE, + brcmf_debug_trace_parser); +} + struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr) { return drvr->wiphy->debugfsdir; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h index 4146faeed3449..95f0f7b2c4a6c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h @@ -29,6 +29,7 @@ #define BRCMF_MSGBUF_VAL 0x00040000 #define BRCMF_PCIE_VAL 0x00080000 #define BRCMF_FWCON_VAL 0x00100000 +#define BRCMF_ULP_VAL 0x00200000 /* set default print format */ #undef pr_fmt @@ -103,6 +104,10 @@ do { \ #endif /* defined(DEBUG) || defined(CONFIG_BRCM_TRACING) */ +#define MSGTRACE_VERSION 1 +#define MSGTRACE_HDR_TYPE_MSG 0 +#define MSGTRACE_HDR_TYPE_LOG 1 + #define brcmf_dbg_hex_dump(test, data, len, fmt, ...) \ do { \ trace_brcmf_hexdump((void *)data, len); \ @@ -120,6 +125,7 @@ void brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn, int (*read_fn)(struct seq_file *seq, void *data)); int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data, size_t len); +int brcmf_debug_fwlog_init(struct brcmf_pub *drvr); #else static inline struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr) { @@ -135,6 +141,25 @@ int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data, { return 0; } + +static inline +int brcmf_debug_fwlog_init(struct brcmf_pub *drvr) +{ + return 0; +} #endif +/* Message trace header */ +struct msgtrace_hdr { + u8 version; + u8 trace_type; + u16 len; /* Len of the trace */ + u32 seqnum; /* Sequence number of message */ + /* Number of discarded bytes because of trace overflow */ + u32 discarded_bytes; + /* Number of discarded printf because of trace overflow */ + u32 discarded_printf; +}; + +#define MSGTRACE_HDRLEN sizeof(struct msgtrace_hdr) #endif /* BRCMFMAC_DEBUG_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c index 7c68d98493246..08535a5e61fec 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c @@ -16,7 +16,6 @@ #include "feature.h" #include "common.h" -#define BRCMF_FW_UNSUPPORTED 23 /* * expand feature list to array of feature strings. @@ -41,8 +40,12 @@ static const struct brcmf_feat_fwcap brcmf_fwcap_map[] = { { BRCMF_FEAT_MONITOR_FLAG, "rtap" }, { BRCMF_FEAT_MONITOR_FMT_RADIOTAP, "rtap" }, { BRCMF_FEAT_DOT11H, "802.11h" }, - { BRCMF_FEAT_SAE, "sae" }, + { BRCMF_FEAT_SAE, "sae " }, { BRCMF_FEAT_FWAUTH, "idauth" }, + { BRCMF_FEAT_SAE_EXT, "sae_ext " }, + { BRCMF_FEAT_FBT, "fbt " }, + { BRCMF_FEAT_OKC, "okc" }, + { BRCMF_FEAT_GCMP, "gcmp" }, }; #ifdef DEBUG @@ -143,7 +146,7 @@ static void brcmf_feat_iovar_int_get(struct brcmf_if *ifp, ifp->fwil_fwerr = true; err = brcmf_fil_iovar_int_get(ifp, name, &data); - if (err == 0) { + if (err != -BRCMF_FW_UNSUPPORTED) { brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]); ifp->drvr->feat_flags |= BIT(id); } else { @@ -248,7 +251,8 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) brcmf_feat_firmware_capabilities(ifp); memset(&gscan_cfg, 0, sizeof(gscan_cfg)); if (drvr->bus_if->chip != BRCM_CC_43430_CHIP_ID && - drvr->bus_if->chip != BRCM_CC_4345_CHIP_ID) + drvr->bus_if->chip != BRCM_CC_4345_CHIP_ID && + drvr->bus_if->chip != CY_CC_43439_CHIP_ID) brcmf_feat_iovar_data_set(ifp, BRCMF_FEAT_GSCAN, "pfn_gscan_cfg", &gscan_cfg, sizeof(gscan_cfg)); @@ -279,6 +283,7 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_RSDB, "rsdb_mode"); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_TDLS, "tdls_enable"); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MFP, "mfp"); + brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_DUMP_OBSS, "dump_obss"); pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER; err = brcmf_fil_iovar_data_get(ifp, "pfn_macaddr", &pfn_mac, diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h index d1f4257af696b..a81cabd975cef 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h @@ -29,6 +29,9 @@ * DOT11H: firmware supports 802.11h * SAE: simultaneous authentication of equals * FWAUTH: Firmware authenticator + * DUMP_OBSS: Firmware has capable to dump obss info to support ACS + * SAE_EXT: SAE be handled by userspace supplicant + * GCMP: firmware has defined GCMP or not. */ #define BRCMF_FEAT_LIST \ BRCMF_FEAT_DEF(MBSS) \ @@ -51,7 +54,12 @@ BRCMF_FEAT_DEF(MONITOR_FMT_HW_RX_HDR) \ BRCMF_FEAT_DEF(DOT11H) \ BRCMF_FEAT_DEF(SAE) \ - BRCMF_FEAT_DEF(FWAUTH) + BRCMF_FEAT_DEF(FWAUTH) \ + BRCMF_FEAT_DEF(DUMP_OBSS) \ + BRCMF_FEAT_DEF(SAE_EXT) \ + BRCMF_FEAT_DEF(FBT) \ + BRCMF_FEAT_DEF(OKC) \ + BRCMF_FEAT_DEF(GCMP) /* * Quirks: diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c index d821a4758f8cf..5704e6f64a8bb 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c @@ -510,7 +510,8 @@ static void brcmf_fw_free_request(struct brcmf_fw_request *req) int i; for (i = 0, item = &req->items[0]; i < req->n_items; i++, item++) { - if (item->type == BRCMF_FW_TYPE_BINARY) + if (item->type == BRCMF_FW_TYPE_BINARY || + item->type == BRCMF_FW_TYPE_TRXSE) release_firmware(item->binary); else if (item->type == BRCMF_FW_TYPE_NVRAM) brcmf_fw_nvram_free(item->nv_data.data); @@ -581,6 +582,7 @@ static int brcmf_fw_complete_request(const struct firmware *fw, ret = brcmf_fw_request_nvram_done(fw, fwctx); break; case BRCMF_FW_TYPE_BINARY: + case BRCMF_FW_TYPE_TRXSE: if (fw) cur->binary = fw; else @@ -613,8 +615,11 @@ static int brcmf_fw_request_firmware(const struct firmware **fw, strlcat(alt_path, fwctx->req->board_type, BRCMF_FW_NAME_LEN); strlcat(alt_path, ".txt", BRCMF_FW_NAME_LEN); - ret = request_firmware(fw, alt_path, fwctx->dev); - if (ret == 0) + ret = request_firmware_direct(fw, alt_path, fwctx->dev); + if (ret) + brcmf_info("no board-specific nvram available (ret=%d), device will use %s\n", + ret, cur->path); + else return ret; } @@ -624,8 +629,19 @@ static int brcmf_fw_request_firmware(const struct firmware **fw, static void brcmf_fw_request_done(const struct firmware *fw, void *ctx) { struct brcmf_fw *fwctx = ctx; + struct brcmf_fw_item *cur = &fwctx->req->items[fwctx->curpos]; + char alt_path[BRCMF_FW_NAME_LEN]; int ret; + if (!fw && cur->type == BRCMF_FW_TYPE_TRXSE) { + strlcpy(alt_path, cur->path, BRCMF_FW_NAME_LEN); + /* strip 'se' from .trxse at the end */ + alt_path[strlen(alt_path) - 2] = 0; + ret = request_firmware(&fw, alt_path, fwctx->dev); + if (!ret) + cur->path = alt_path; + } + ret = brcmf_fw_complete_request(fw, fwctx); while (ret == 0 && ++fwctx->curpos < fwctx->req->n_items) { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h index 46c66415b4a67..53141a5e853df 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h @@ -11,6 +11,8 @@ #define BRCMF_FW_DEFAULT_PATH "brcm/" +#define CY_FW_DEFAULT_PATH "cypress/" + /** * struct brcmf_firmware_mapping - Used to map chipid/revmask to firmware * filename and nvram filename. Each bus type implementation should create @@ -32,6 +34,16 @@ static const char BRCM_ ## fw_name ## _FIRMWARE_BASENAME[] = \ BRCMF_FW_DEFAULT_PATH fw_base; \ MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH fw_base ".bin") +#define CY_FW_DEF(fw_name, fw_base) \ +static const char BRCM_ ## fw_name ## _FIRMWARE_BASENAME[] = \ + CY_FW_DEFAULT_PATH fw_base; \ +MODULE_FIRMWARE(CY_FW_DEFAULT_PATH fw_base ".bin") + +#define CY_FW_TRXSE_DEF(fw_name, fw_base) \ +static const char BRCM_ ## fw_name ## _FIRMWARE_BASENAME[] = \ + CY_FW_DEFAULT_PATH fw_base; \ +MODULE_FIRMWARE(CY_FW_DEFAULT_PATH fw_base ".trxse") + #define BRCMF_FW_ENTRY(chipid, mask, name) \ { chipid, mask, BRCM_ ## name ## _FIRMWARE_BASENAME } @@ -39,7 +51,8 @@ void brcmf_fw_nvram_free(void *nvram); enum brcmf_fw_type { BRCMF_FW_TYPE_BINARY, - BRCMF_FW_TYPE_NVRAM + BRCMF_FW_TYPE_NVRAM, + BRCMF_FW_TYPE_TRXSE }; struct brcmf_fw_item { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c index 096f6b969dd83..e1127d7e086d5 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c @@ -419,7 +419,6 @@ void brcmf_flowring_configure_addr_mode(struct brcmf_flowring *flow, int ifidx, flowid = flow->hash[i].flowid; if (flow->rings[flowid]->status != RING_OPEN) continue; - flow->rings[flowid]->status = RING_CLOSING; brcmf_msgbuf_delete_flowring(drvr, flowid); } } @@ -458,10 +457,8 @@ void brcmf_flowring_delete_peer(struct brcmf_flowring *flow, int ifidx, if ((sta || (memcmp(hash[i].mac, peer, ETH_ALEN) == 0)) && (hash[i].ifidx == ifidx)) { flowid = flow->hash[i].flowid; - if (flow->rings[flowid]->status == RING_OPEN) { - flow->rings[flowid]->status = RING_CLOSING; + if (flow->rings[flowid]->status == RING_OPEN) brcmf_msgbuf_delete_flowring(drvr, flowid); - } } } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c index 430d2cca98b33..6f3eab947bb58 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c @@ -355,26 +355,42 @@ int brcmf_fweh_activate_events(struct brcmf_if *ifp) { struct brcmf_pub *drvr = ifp->drvr; int i, err; - s8 eventmask[BRCMF_EVENTING_MASK_LEN]; + struct eventmsgs_ext *eventmask_msg; + u32 msglen; + + msglen = EVENTMSGS_EXT_STRUCT_SIZE + BRCMF_EVENTING_MASK_LEN; + eventmask_msg = kzalloc(msglen, GFP_KERNEL); + if (!eventmask_msg) + return -ENOMEM; - memset(eventmask, 0, sizeof(eventmask)); for (i = 0; i < BRCMF_E_LAST; i++) { if (ifp->drvr->fweh.evt_handler[i]) { brcmf_dbg(EVENT, "enable event %s\n", brcmf_fweh_event_name(i)); - setbit(eventmask, i); + setbit(eventmask_msg->mask, i); } } /* want to handle IF event as well */ brcmf_dbg(EVENT, "enable event IF\n"); - setbit(eventmask, BRCMF_E_IF); + setbit(eventmask_msg->mask, BRCMF_E_IF); + + eventmask_msg->ver = EVENTMSGS_VER; + eventmask_msg->command = EVENTMSGS_SET_MASK; + eventmask_msg->len = BRCMF_EVENTING_MASK_LEN; + + err = brcmf_fil_iovar_data_set(ifp, "event_msgs_ext", eventmask_msg, + msglen); + if (!err) + goto end; - err = brcmf_fil_iovar_data_set(ifp, "event_msgs", - eventmask, BRCMF_EVENTING_MASK_LEN); + err = brcmf_fil_iovar_data_set(ifp, "event_msgs", eventmask_msg->mask, + BRCMF_EVENTING_MASK_LEN); if (err) bphy_err(drvr, "Set event_msgs error (%d)\n", err); +end: + kfree(eventmask_msg); return err; } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h index 48414e8b93890..f92700237d0c8 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h @@ -90,7 +90,13 @@ struct brcmf_cfg80211_info; BRCMF_ENUM_DEF(FIFO_CREDIT_MAP, 74) \ BRCMF_ENUM_DEF(ACTION_FRAME_RX, 75) \ BRCMF_ENUM_DEF(TDLS_PEER_EVENT, 92) \ - BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127) + BRCMF_ENUM_DEF(PHY_TEMP, 111) \ + BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127) \ + BRCMF_ENUM_DEF(ULP, 146) \ + BRCMF_ENUM_DEF(EXT_AUTH_REQ, 187) \ + BRCMF_ENUM_DEF(EXT_AUTH_FRAME_RX, 188) \ + BRCMF_ENUM_DEF(MGMT_FRAME_TXSTATUS, 189) \ + BRCMF_ENUM_DEF(MGMT_FRAME_OFF_CHAN_COMPLETE, 190) #define BRCMF_ENUM_DEF(id, val) \ BRCMF_E_##id = (val), @@ -102,7 +108,7 @@ enum brcmf_fweh_event_code { * minimum length check in device firmware so it is * hard-coded here. */ - BRCMF_E_LAST = 139 + BRCMF_E_LAST = 191 }; #undef BRCMF_ENUM_DEF @@ -283,6 +289,28 @@ struct brcmf_if_event { u8 role; }; +enum event_msgs_ext_command { + EVENTMSGS_NONE = 0, + EVENTMSGS_SET_BIT = 1, + EVENTMSGS_RESET_BIT = 2, + EVENTMSGS_SET_MASK = 3 +}; + +#define EVENTMSGS_VER 1 +#define EVENTMSGS_EXT_STRUCT_SIZE offsetof(struct eventmsgs_ext, mask[0]) + +/* len- for SET it would be mask size from the application to the firmware */ +/* for GET it would be actual firmware mask size */ +/* maxgetsize - is only used for GET. indicate max mask size that the */ +/* application can read from the firmware */ +struct eventmsgs_ext { + u8 ver; + u8 command; + u8 len; + u8 maxgetsize; + u8 mask[1]; +}; + typedef int (*brcmf_fweh_handler_t)(struct brcmf_if *ifp, const struct brcmf_event_msg *evtmsg, void *data); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h index ae4cf43729086..9ed7e56bcb9e2 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h @@ -71,6 +71,7 @@ #define BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON 201 #define BRCMF_C_SET_ASSOC_PREFER 205 #define BRCMF_C_GET_VALID_CHANNELS 217 +#define BRCMF_C_GET_FAKEFRAG 218 #define BRCMF_C_SET_FAKEFRAG 219 #define BRCMF_C_GET_KEY_PRIMARY 235 #define BRCMF_C_SET_KEY_PRIMARY 236 @@ -79,6 +80,9 @@ #define BRCMF_C_SET_VAR 263 #define BRCMF_C_SET_WSEC_PMK 268 +#define BRCMF_FW_BADARG 2 +#define BRCMF_FW_UNSUPPORTED 23 + s32 brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len); s32 brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len); s32 brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h index 2e31cc10c1954..f05384c87da22 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -59,6 +59,8 @@ #define BRCMF_SCANTYPE_PASSIVE 1 #define BRCMF_WSEC_MAX_PSK_LEN 32 +#define BRCMF_WSEC_PMK_LEN_SUITEB_192 48 +#define BRCMF_WSEC_MAX_PMK_LEN 64 /* SUITE-B-192's PMK is 48 bytes */ #define BRCMF_WSEC_PASSPHRASE BIT(0) #define BRCMF_WSEC_MAX_SAE_PASSWORD_LEN 128 @@ -135,9 +137,22 @@ /* Link Down indication in WoWL mode: */ #define BRCMF_WOWL_LINKDOWN (1 << 31) -#define BRCMF_WOWL_MAXPATTERNS 8 +#define BRCMF_WOWL_MAXPATTERNS 16 #define BRCMF_WOWL_MAXPATTERNSIZE 128 +enum { + BRCMF_UNICAST_FILTER_NUM = 0, + BRCMF_BROADCAST_FILTER_NUM, + BRCMF_MULTICAST4_FILTER_NUM, + BRCMF_MULTICAST6_FILTER_NUM, + BRCMF_MDNS_FILTER_NUM, + BRCMF_ARP_FILTER_NUM, + BRCMF_BROADCAST_ARP_FILTER_NUM, + MAX_PKT_FILTER_COUNT +}; + +#define MAX_PKTFILTER_PATTERN_SIZE 16 + #define BRCMF_COUNTRY_BUF_SZ 4 #define BRCMF_ANT_MAX 4 @@ -169,6 +184,11 @@ #define BRCMF_HE_CAP_MCS_MAP_NSS_MAX 8 +#define BRCMF_EXTAUTH_START 1 +#define BRCMF_EXTAUTH_ABORT 2 +#define BRCMF_EXTAUTH_FAIL 3 +#define BRCMF_EXTAUTH_SUCCESS 4 + /* MAX_CHUNK_LEN is the maximum length for data passing to firmware in each * ioctl. It is relatively small because firmware has small maximum size input * playload restriction for ioctls. @@ -184,6 +204,8 @@ #define DL_TYPE_CLM 2 +#define MAX_RSSI_LEVELS 8 + /* join preference types for join_pref iovar */ enum brcmf_join_pref_types { BRCMF_JOIN_PREF_RSSI = 1, @@ -517,7 +539,7 @@ struct brcmf_wsec_key_le { struct brcmf_wsec_pmk_le { __le16 key_len; __le16 flags; - u8 key[2 * BRCMF_WSEC_MAX_PSK_LEN + 1]; + u8 key[2 * BRCMF_WSEC_MAX_PMK_LEN + 1]; }; /** @@ -531,6 +553,47 @@ struct brcmf_wsec_sae_pwd_le { u8 key[BRCMF_WSEC_MAX_SAE_PASSWORD_LEN]; }; +/** + * struct brcmf_auth_req_status_le - external auth request and status update + * + * @flags: flags for external auth status + * @peer_mac: peer MAC address + * @ssid_len: length of ssid + * @ssid: ssid characters + */ +struct brcmf_auth_req_status_le { + __le16 flags; + u8 peer_mac[ETH_ALEN]; + __le32 ssid_len; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + u8 pmkid[WLAN_PMKID_LEN]; +}; + +/** + * struct brcmf_mf_params_le - management frame parameters for mgmt_frame iovar + * + * @version: version of the iovar + * @dwell_time: dwell duration in ms + * @len: length of frame data + * @frame_control: frame control + * @channel: channel + * @da: peer MAC address + * @bssid: BSS network identifier + * @packet_id: packet identifier + * @data: frame data + */ +struct brcmf_mf_params_le { + __le32 version; + __le32 dwell_time; + __le16 len; + __le16 frame_control; + __le16 channel; + u8 da[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + __le32 packet_id; + u8 data[1]; +}; + /* Used to get specific STA parameters */ struct brcmf_scb_val_le { __le32 val; @@ -1024,4 +1087,19 @@ struct brcmf_gscan_config { struct brcmf_gscan_bucket_config bucket[1]; }; +/* BRCM_E_RSSI event data */ +struct wl_event_data_rssi { + s32 rssi; + s32 snr; + s32 noise; +}; + +/** RSSI event notification configuration. */ +struct wl_rssi_event { + u32 rate_limit_msec; + u8 num_rssi_levels; + s8 rssi_levels[MAX_RSSI_LEVELS]; + s8 pad[3]; +}; + #endif /* FWIL_TYPES_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c index 437e83ea8902d..8c06160a18bab 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c @@ -502,6 +502,9 @@ struct brcmf_fws_info { bool creditmap_received; u8 mode; bool avoid_queueing; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) + int fifo_init_credit[BRCMF_FWS_FIFO_COUNT]; +#endif }; #define BRCMF_FWS_TLV_DEF(name, id, len) \ @@ -621,7 +624,6 @@ static inline int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h, static void brcmf_fws_psq_flush(struct brcmf_fws_info *fws, struct pktq *q, int ifidx) { - struct brcmf_fws_hanger_item *hi; bool (*matchfn)(struct sk_buff *, void *) = NULL; struct sk_buff *skb; int prec; @@ -633,9 +635,6 @@ static void brcmf_fws_psq_flush(struct brcmf_fws_info *fws, struct pktq *q, skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx); while (skb) { hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); - hi = &fws->hanger.items[hslot]; - WARN_ON(skb != hi->pkt); - hi->state = BRCMF_FWS_HANGER_ITEM_STATE_FREE; brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, true); brcmu_pkt_buf_free_skb(skb); @@ -1617,9 +1616,13 @@ static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp, fws->fifo_credit_map |= 1 << i; else fws->fifo_credit_map &= ~(1 << i); + WARN_ONCE(fws->fifo_credit[i] < 0, "fifo_credit[%d] is negative(%d)\n", i, fws->fifo_credit[i]); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) + fws->fifo_init_credit[i] = fws->fifo_credit[i]; +#endif } brcmf_fws_schedule_deq(fws); brcmf_fws_unlock(fws); @@ -2197,6 +2200,38 @@ void brcmf_fws_del_interface(struct brcmf_if *ifp) brcmf_fws_unlock(fws); } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) +static bool brcmf_fws_ismultistream(struct brcmf_fws_info *fws) +{ + bool ret = false; + u8 credit_usage = 0; + + /* Check only for BE, VI and VO traffic */ + u32 delay_map = fws->fifo_delay_map & + ((1 << BRCMF_FWS_FIFO_AC_BE) | + (1 << BRCMF_FWS_FIFO_AC_VI) | + (1 << BRCMF_FWS_FIFO_AC_VO)); + + if (hweight_long(delay_map) > 1) { + ret = true; + } else { + if (fws->fifo_credit[BRCMF_FWS_FIFO_AC_BE] < + fws->fifo_init_credit[BRCMF_FWS_FIFO_AC_BE]) + credit_usage++; + if (fws->fifo_credit[BRCMF_FWS_FIFO_AC_VI] < + fws->fifo_init_credit[BRCMF_FWS_FIFO_AC_VI]) + credit_usage++; + if (fws->fifo_credit[BRCMF_FWS_FIFO_AC_VO] < + fws->fifo_init_credit[BRCMF_FWS_FIFO_AC_VO]) + credit_usage++; + + if (credit_usage > 1) + ret = true; + } + return ret; +} +#endif + static void brcmf_fws_dequeue_worker(struct work_struct *worker) { struct brcmf_fws_info *fws; @@ -2210,6 +2245,13 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker) fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work); drvr = fws->drvr; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) + if (brcmf_fws_ismultistream(fws)) + drvr->bus_if->allow_skborphan = false; + else + drvr->bus_if->allow_skborphan = true; +#endif + brcmf_fws_lock(fws); for (fifo = BRCMF_FWS_FIFO_BCMC; fifo >= 0 && !fws->bus_flow_blocked; fifo--) { @@ -2475,7 +2517,8 @@ bool brcmf_fws_fc_active(struct brcmf_fws_info *fws) return fws->fcmode != BRCMF_FWS_FCMODE_NONE; } -void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb) +void brcmf_fws_bustxcomplete(struct brcmf_fws_info *fws, struct sk_buff *skb, + bool success) { u32 hslot; @@ -2483,11 +2526,13 @@ void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb) brcmu_pkt_buf_free_skb(skb); return; } - brcmf_fws_lock(fws); - hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); - brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, hslot, 0, 0, - 1); - brcmf_fws_unlock(fws); + if (!success) { + brcmf_fws_lock(fws); + hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); + brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, + hslot, 0, 0, 1); + brcmf_fws_unlock(fws); + } } void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h index 50e424b5880de..c3b2d3691c43c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h @@ -40,7 +40,8 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb); void brcmf_fws_reset_interface(struct brcmf_if *ifp); void brcmf_fws_add_interface(struct brcmf_if *ifp); void brcmf_fws_del_interface(struct brcmf_if *ifp); -void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb); +void brcmf_fws_bustxcomplete(struct brcmf_fws_info *fws, struct sk_buff *skb, + bool success); void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked); void brcmf_fws_rxreorder(struct brcmf_if *ifp, struct sk_buff *skb, bool inirq); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c index 7c8e08ee8f0ff..920ec9314e323 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -23,6 +24,7 @@ #include "flowring.h" #include "bus.h" #include "tracepoint.h" +#include "pcie.h" #define MSGBUF_IOCTL_RESP_TIMEOUT msecs_to_jiffies(2000) @@ -47,6 +49,8 @@ #define MSGBUF_TYPE_RX_CMPLT 0x12 #define MSGBUF_TYPE_LPBK_DMAXFER 0x13 #define MSGBUF_TYPE_LPBK_DMAXFER_CMPLT 0x14 +#define MSGBUF_TYPE_H2D_MAILBOX_DATA 0x23 +#define MSGBUF_TYPE_D2H_MAILBOX_DATA 0x24 #define NR_TX_PKTIDS 2048 #define NR_RX_PKTIDS 1024 @@ -71,6 +75,7 @@ #define BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS 32 #define BRCMF_MSGBUF_UPDATE_RX_PTR_THRS 48 +#define BRCMF_MAX_TXSTATUS_WAIT_RETRIES 10 struct msgbuf_common_hdr { u8 msgtype; @@ -103,6 +108,12 @@ struct msgbuf_tx_msghdr { __le32 rsvd0; }; +struct msgbuf_h2d_mbdata { + struct msgbuf_common_hdr msg; + __le32 mbdata; + __le16 rsvd0[7]; +}; + struct msgbuf_rx_bufpost { struct msgbuf_common_hdr msg; __le16 metadata_buf_len; @@ -217,6 +228,13 @@ struct msgbuf_flowring_flush_resp { __le32 rsvd0[3]; }; +struct msgbuf_d2h_mailbox_data { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + __le32 mbdata; + __le32 rsvd0[2]; +} d2h_mailbox_data_t; + struct brcmf_msgbuf_work_item { struct list_head queue; u32 flowid; @@ -289,6 +307,8 @@ struct brcmf_msgbuf_pktids { }; static void brcmf_msgbuf_rxbuf_ioctlresp_post(struct brcmf_msgbuf *msgbuf); +static void brcmf_msgbuf_process_d2h_mbdata(struct brcmf_msgbuf *msgbuf, + void *buf); static struct brcmf_msgbuf_pktids * @@ -423,6 +443,34 @@ static void brcmf_msgbuf_release_pktids(struct brcmf_msgbuf *msgbuf) msgbuf->tx_pktids); } +int brcmf_msgbuf_tx_mbdata(struct brcmf_pub *drvr, u32 mbdata) +{ + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + struct brcmf_commonring *commonring; + struct msgbuf_h2d_mbdata *h2d_mbdata; + void *ret_ptr; + int err; + + commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT]; + brcmf_commonring_lock(commonring); + ret_ptr = brcmf_commonring_reserve_for_write(commonring); + if (!ret_ptr) { + brcmf_err("Failed to reserve space in commonring\n"); + brcmf_commonring_unlock(commonring); + return -ENOMEM; + } + h2d_mbdata = (struct msgbuf_h2d_mbdata *)ret_ptr; + memset(h2d_mbdata, 0, sizeof(*h2d_mbdata)); + + h2d_mbdata->msg.msgtype = MSGBUF_TYPE_H2D_MAILBOX_DATA; + h2d_mbdata->mbdata = cpu_to_le32(mbdata); + + err = brcmf_commonring_write_complete(commonring); + brcmf_commonring_unlock(commonring); + + return err; +} + static int brcmf_msgbuf_tx_ioctl(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, uint len) @@ -719,6 +767,7 @@ static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u16 flowid) brcmf_flowring_qlen(flow, flowid)); break; } + skb_tx_timestamp(skb); skb_orphan(skb); if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev, msgbuf->tx_pktids, skb, ETH_HLEN, @@ -807,8 +856,12 @@ static int brcmf_msgbuf_tx_queue_data(struct brcmf_pub *drvr, int ifidx, flowid = brcmf_flowring_lookup(flow, eh->h_dest, skb->priority, ifidx); if (flowid == BRCMF_FLOWRING_INVALID_ID) { flowid = brcmf_msgbuf_flowring_create(msgbuf, ifidx, skb); - if (flowid == BRCMF_FLOWRING_INVALID_ID) + if (flowid == BRCMF_FLOWRING_INVALID_ID) { return -ENOMEM; + } else { + brcmf_flowring_enqueue(flow, flowid, skb); + return 0; + } } queue_count = brcmf_flowring_enqueue(flow, flowid, skb); force = ((queue_count % BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS) == 0); @@ -1141,7 +1194,8 @@ brcmf_msgbuf_process_rx_complete(struct brcmf_msgbuf *msgbuf, void *buf) { struct brcmf_pub *drvr = msgbuf->drvr; struct msgbuf_rx_complete *rx_complete; - struct sk_buff *skb; + struct sk_buff *skb, *cpskb = NULL; + struct ethhdr *eh; u16 data_offset; u16 buflen; u16 flags; @@ -1190,6 +1244,34 @@ brcmf_msgbuf_process_rx_complete(struct brcmf_msgbuf *msgbuf, void *buf) return; } + if (ifp->isap && ifp->fmac_pkt_fwd_en) { + eh = (struct ethhdr *)(skb->data); + skb_set_network_header(skb, sizeof(struct ethhdr)); + skb->protocol = eh->h_proto; + skb->priority = cfg80211_classify8021d(skb, NULL); + if (is_unicast_ether_addr(eh->h_dest)) { + if (brcmf_find_sta(ifp, eh->h_dest)) { + /* determine the priority */ + if (skb->priority == 0 || skb->priority > 7) { + skb->priority = + cfg80211_classify8021d(skb, + NULL); + } + brcmf_proto_tx_queue_data(ifp->drvr, + ifp->ifidx, skb); + return; + } + } else { + cpskb = pskb_copy(skb, GFP_ATOMIC); + if (cpskb) { + brcmf_proto_tx_queue_data(ifp->drvr, + ifp->ifidx, + cpskb); + } else { + brcmf_err("Unable to do skb copy\n"); + } + } + } skb->protocol = eth_type_trans(skb, ifp->ndev); brcmf_netif_rx(ifp, skb, false); } @@ -1277,6 +1359,21 @@ brcmf_msgbuf_process_flow_ring_delete_response(struct brcmf_msgbuf *msgbuf, brcmf_msgbuf_remove_flowring(msgbuf, flowid); } +static void +brcmf_msgbuf_process_d2h_mbdata(struct brcmf_msgbuf *msgbuf, + void *buf) +{ + struct msgbuf_d2h_mailbox_data *d2h_mbdata; + + d2h_mbdata = (struct msgbuf_d2h_mailbox_data *)buf; + + if (!d2h_mbdata) { + brcmf_err("d2h_mbdata is null\n"); + return; + } + + brcmf_pcie_handle_mb_data(msgbuf->drvr->bus_if, d2h_mbdata->mbdata); +} static void brcmf_msgbuf_process_msgtype(struct brcmf_msgbuf *msgbuf, void *buf) { @@ -1320,6 +1417,11 @@ static void brcmf_msgbuf_process_msgtype(struct brcmf_msgbuf *msgbuf, void *buf) brcmf_dbg(MSGBUF, "MSGBUF_TYPE_RX_CMPLT\n"); brcmf_msgbuf_process_rx_complete(msgbuf, buf); break; + case MSGBUF_TYPE_D2H_MAILBOX_DATA: + brcmf_dbg(MSGBUF, "MSGBUF_TYPE_D2H_MAILBOX_DATA\n"); + brcmf_msgbuf_process_d2h_mbdata(msgbuf, buf); + break; + default: bphy_err(drvr, "Unsupported msgtype %d\n", msg->msgtype); break; @@ -1396,9 +1498,27 @@ void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u16 flowid) struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; struct msgbuf_tx_flowring_delete_req *delete; struct brcmf_commonring *commonring; + struct brcmf_commonring *commonring_del = msgbuf->flowrings[flowid]; + struct brcmf_flowring *flow = msgbuf->flow; void *ret_ptr; u8 ifidx; int err; + int retry = BRCMF_MAX_TXSTATUS_WAIT_RETRIES; + + /* make sure it is not in txflow */ + brcmf_commonring_lock(commonring_del); + flow->rings[flowid]->status = RING_CLOSING; + brcmf_commonring_unlock(commonring_del); + + /* wait for commonring txflow finished */ + while (retry && atomic_read(&commonring_del->outstanding_tx)) { + usleep_range(5000, 10000); + retry--; + } + if (!retry) { + brcmf_err("timed out waiting for txstatus\n"); + atomic_set(&commonring_del->outstanding_tx, 0); + } /* no need to submit if firmware can not be reached */ if (drvr->bus_if->state != BRCMF_BUS_UP) { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h index 2e322edbb9070..ff0b5c1aa8741 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h @@ -39,5 +39,6 @@ static inline int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) } static inline void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr) {} #endif +int brcmf_msgbuf_tx_mbdata(struct brcmf_pub *drvr, u32 mbdata); #endif /* BRCMFMAC_MSGBUF_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c index a7554265f95f8..a1d6c80b74d85 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c @@ -19,7 +19,8 @@ void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type, struct device_node *root, *np = dev->of_node; int irq; u32 irqf; - u32 val; + u32 val32; + u16 val16; /* Set board-type to the first string of the machine compatible prop */ root = of_find_node_by_path("/"); @@ -47,8 +48,15 @@ void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type, !of_device_is_compatible(np, "brcm,bcm4329-fmac")) return; - if (of_property_read_u32(np, "brcm,drive-strength", &val) == 0) - sdio->drive_strength = val; + if (of_property_read_u32(np, "brcm,drive-strength", &val32) == 0) + sdio->drive_strength = val32; + + sdio->broken_sg_support = of_property_read_bool(np, + "brcm,broken_sg_support"); + if (of_property_read_u16(np, "brcm,sd_head_align", &val16) == 0) + sdio->sd_head_align = val16; + if (of_property_read_u16(np, "brcm,sd_sgentry_align", &val16) == 0) + sdio->sd_sgentry_align = val16; /* make sure there are interrupts defined in the node */ if (!of_find_property(np, "interrupts", NULL)) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c index ec6fc7a150a6a..28fd3ebf20e6c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c @@ -63,6 +63,7 @@ #define P2P_AF_TX_MAX_RETRY 5 #define P2P_AF_MAX_WAIT_TIME msecs_to_jiffies(2000) #define P2P_INVALID_CHANNEL -1 +#define P2P_INVALID_CHANSPEC 0 #define P2P_CHANNEL_SYNC_RETRY 5 #define P2P_AF_FRM_SCAN_MAX_WAIT msecs_to_jiffies(450) #define P2P_DEFAULT_SLEEP_TIME_VSDB 200 @@ -231,7 +232,35 @@ static bool brcmf_p2p_is_pub_action(void *frame, u32 frame_len) if (pact_frm->category == P2P_PUB_AF_CATEGORY && pact_frm->action == P2P_PUB_AF_ACTION && pact_frm->oui_type == P2P_VER && - memcmp(pact_frm->oui, P2P_OUI, P2P_OUI_LEN) == 0) + memcmp(pact_frm->oui, WFA_OUI, P2P_OUI_LEN) == 0) + return true; + + return false; +} + +/** + * brcmf_p2p_is_dpp_pub_action() - true if dpp public type frame. + * + * @frame: action frame data. + * @frame_len: length of action frame data. + * + * Determine if action frame is dpp public action type + */ +static bool brcmf_p2p_is_dpp_pub_action(void *frame, u32 frame_len) +{ + struct brcmf_p2p_pub_act_frame *pact_frm; + + if (!frame) + return false; + + pact_frm = (struct brcmf_p2p_pub_act_frame *)frame; + if (frame_len < sizeof(struct brcmf_p2p_pub_act_frame) - 1) + return false; + + if (pact_frm->category == WLAN_CATEGORY_PUBLIC && + pact_frm->action == WLAN_PUB_ACTION_VENDOR_SPECIFIC && + pact_frm->oui_type == DPP_VER && + memcmp(pact_frm->oui, WFA_OUI, TLV_OUI_LEN) == 0) return true; return false; @@ -894,7 +923,8 @@ int brcmf_p2p_scan_prep(struct wiphy *wiphy, { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_p2p_info *p2p = &cfg->p2p; - int err; + int err = 0; + struct brcmu_chan ch; if (brcmf_p2p_scan_is_p2p_request(request)) { /* find my listen channel */ @@ -903,7 +933,12 @@ int brcmf_p2p_scan_prep(struct wiphy *wiphy, if (err < 0) return err; - p2p->afx_hdl.my_listen_chan = err; + ch.band = BRCMU_CHAN_BAND_2G; + ch.bw = BRCMU_CHAN_BW_20; + ch.sb = BRCMU_CHAN_SB_NONE; + ch.chnum = err; + p2p->cfg->d11inf.encchspec(&ch); + p2p->afx_hdl.my_listen_chan = ch.chspec; clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status); brcmf_dbg(INFO, "P2P: GO_NEG_PHASE status cleared\n"); @@ -912,10 +947,14 @@ int brcmf_p2p_scan_prep(struct wiphy *wiphy, if (err) return err; + vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif; + /* override .run_escan() callback. */ cfg->escan_info.run = brcmf_p2p_run_escan; } - return 0; + err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBREQ_FLAG, + request->ie, request->ie_len); + return err; } @@ -923,16 +962,15 @@ int brcmf_p2p_scan_prep(struct wiphy *wiphy, * brcmf_p2p_discover_listen() - set firmware to discover listen state. * * @p2p: p2p device. - * @channel: channel nr for discover listen. + * @chspec: chspec for discover listen. * @duration: time in ms to stay on channel. * */ static s32 -brcmf_p2p_discover_listen(struct brcmf_p2p_info *p2p, u16 channel, u32 duration) +brcmf_p2p_discover_listen(struct brcmf_p2p_info *p2p, u16 chspec, u32 duration) { struct brcmf_pub *drvr = p2p->cfg->pub; struct brcmf_cfg80211_vif *vif; - struct brcmu_chan ch; s32 err = 0; vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif; @@ -948,11 +986,8 @@ brcmf_p2p_discover_listen(struct brcmf_p2p_info *p2p, u16 channel, u32 duration) goto exit; } - ch.chnum = channel; - ch.bw = BRCMU_CHAN_BW_20; - p2p->cfg->d11inf.encchspec(&ch); err = brcmf_p2p_set_discover_state(vif->ifp, WL_P2P_DISC_ST_LISTEN, - ch.chspec, (u16)duration); + chspec, (u16)duration); if (!err) { set_bit(BRCMF_P2P_STATUS_DISCOVER_LISTEN, &p2p->status); p2p->remain_on_channel_cookie++; @@ -978,19 +1013,17 @@ int brcmf_p2p_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_p2p_info *p2p = &cfg->p2p; s32 err; - u16 channel_nr; - - channel_nr = ieee80211_frequency_to_channel(channel->center_freq); - brcmf_dbg(TRACE, "Enter, channel: %d, duration ms (%d)\n", channel_nr, - duration); err = brcmf_p2p_enable_discovery(p2p); if (err) goto exit; - err = brcmf_p2p_discover_listen(p2p, channel_nr, duration); + err = brcmf_p2p_discover_listen(p2p, + channel_to_chanspec(&cfg->d11inf, channel), duration); if (err) goto exit; + p2p->remin_on_channel_wdev = wdev; + memcpy(&p2p->remain_on_channel, channel, sizeof(*channel)); *cookie = p2p->remain_on_channel_cookie; cfg80211_ready_on_channel(wdev, *cookie, channel, duration, GFP_KERNEL); @@ -1014,6 +1047,7 @@ int brcmf_p2p_notify_listen_complete(struct brcmf_if *ifp, { struct brcmf_cfg80211_info *cfg = ifp->drvr->config; struct brcmf_p2p_info *p2p = &cfg->p2p; + struct wireless_dev *wdev = p2p->remin_on_channel_wdev; brcmf_dbg(TRACE, "Enter\n"); if (test_and_clear_bit(BRCMF_P2P_STATUS_DISCOVER_LISTEN, @@ -1026,10 +1060,16 @@ int brcmf_p2p_notify_listen_complete(struct brcmf_if *ifp, complete(&p2p->wait_next_af); } - cfg80211_remain_on_channel_expired(&ifp->vif->wdev, + wdev = p2p->remin_on_channel_wdev ? + p2p->remin_on_channel_wdev : + &ifp->vif->wdev; + + cfg80211_remain_on_channel_expired(wdev, p2p->remain_on_channel_cookie, &p2p->remain_on_channel, GFP_KERNEL); + p2p->remin_on_channel_wdev = NULL; + } return 0; } @@ -1054,12 +1094,12 @@ void brcmf_p2p_cancel_remain_on_channel(struct brcmf_if *ifp) * brcmf_p2p_act_frm_search() - search function for action frame. * * @p2p: p2p device. - * @channel: channel on which action frame is to be trasmitted. + * @chspec: chspec on which action frame is to be trasmitted. * * search function to reach at common channel to send action frame. When * channel is 0 then all social channels will be used to send af */ -static s32 brcmf_p2p_act_frm_search(struct brcmf_p2p_info *p2p, u16 channel) +static s32 brcmf_p2p_act_frm_search(struct brcmf_p2p_info *p2p, u16 chspec) { struct brcmf_pub *drvr = p2p->cfg->pub; s32 err; @@ -1070,7 +1110,7 @@ static s32 brcmf_p2p_act_frm_search(struct brcmf_p2p_info *p2p, u16 channel) brcmf_dbg(TRACE, "Enter\n"); - if (channel) + if (chspec) channel_cnt = AF_PEER_SEARCH_CNT; else channel_cnt = SOCIAL_CHAN_CNT; @@ -1081,14 +1121,13 @@ static s32 brcmf_p2p_act_frm_search(struct brcmf_p2p_info *p2p, u16 channel) err = -ENOMEM; goto exit; } - ch.bw = BRCMU_CHAN_BW_20; - if (channel) { - ch.chnum = channel; - p2p->cfg->d11inf.encchspec(&ch); - /* insert same channel to the chan_list */ + + if (chspec) { for (i = 0; i < channel_cnt; i++) - default_chan_list[i] = ch.chspec; + default_chan_list[i] = chspec; } else { + ch.band = BRCMU_CHAN_BAND_2G; + ch.bw = BRCMU_CHAN_BW_20; ch.chnum = SOCIAL_CHAN_1; p2p->cfg->d11inf.encchspec(&ch); default_chan_list[0] = ch.chspec; @@ -1147,7 +1186,7 @@ static void brcmf_p2p_afx_handler(struct work_struct *work) * @p2p: p2p device info struct. * */ -static s32 brcmf_p2p_af_searching_channel(struct brcmf_p2p_info *p2p) +static u16 brcmf_p2p_af_searching_channel(struct brcmf_p2p_info *p2p) { struct afx_hdl *afx_hdl = &p2p->afx_hdl; struct brcmf_cfg80211_vif *pri_vif; @@ -1160,14 +1199,14 @@ static s32 brcmf_p2p_af_searching_channel(struct brcmf_p2p_info *p2p) reinit_completion(&afx_hdl->act_frm_scan); set_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status); afx_hdl->is_active = true; - afx_hdl->peer_chan = P2P_INVALID_CHANNEL; + afx_hdl->peer_chan = P2P_INVALID_CHANSPEC; /* Loop to wait until we find a peer's channel or the * pending action frame tx is cancelled. */ retry = 0; while ((retry < P2P_CHANNEL_SYNC_RETRY) && - (afx_hdl->peer_chan == P2P_INVALID_CHANNEL)) { + (afx_hdl->peer_chan == P2P_INVALID_CHANSPEC)) { afx_hdl->is_listen = false; brcmf_dbg(TRACE, "Scheduling action frame for sending.. (%d)\n", retry); @@ -1175,13 +1214,13 @@ static s32 brcmf_p2p_af_searching_channel(struct brcmf_p2p_info *p2p) schedule_work(&afx_hdl->afx_work); wait_for_completion_timeout(&afx_hdl->act_frm_scan, P2P_AF_FRM_SCAN_MAX_WAIT); - if ((afx_hdl->peer_chan != P2P_INVALID_CHANNEL) || + if ((afx_hdl->peer_chan != P2P_INVALID_CHANSPEC) || (!test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status))) break; if (afx_hdl->my_listen_chan) { - brcmf_dbg(TRACE, "Scheduling listen peer, channel=%d\n", + brcmf_dbg(TRACE, "Scheduling listen peer, chanspec=0x%04x\n", afx_hdl->my_listen_chan); /* listen on my listen channel */ afx_hdl->is_listen = true; @@ -1189,7 +1228,7 @@ static s32 brcmf_p2p_af_searching_channel(struct brcmf_p2p_info *p2p) wait_for_completion_timeout(&afx_hdl->act_frm_scan, P2P_AF_FRM_SCAN_MAX_WAIT); } - if ((afx_hdl->peer_chan != P2P_INVALID_CHANNEL) || + if ((afx_hdl->peer_chan != P2P_INVALID_CHANSPEC) || (!test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status))) break; @@ -1203,7 +1242,7 @@ static s32 brcmf_p2p_af_searching_channel(struct brcmf_p2p_info *p2p) msleep(P2P_DEFAULT_SLEEP_TIME_VSDB); } - brcmf_dbg(TRACE, "Completed search/listen peer_chan=%d\n", + brcmf_dbg(TRACE, "Completed search/listen peer_chan=0x%4x\n", afx_hdl->peer_chan); afx_hdl->is_active = false; @@ -1236,7 +1275,7 @@ bool brcmf_p2p_scan_finding_common_channel(struct brcmf_cfg80211_info *cfg, if (bi == NULL) { brcmf_dbg(TRACE, "ACTION FRAME SCAN Done\n"); - if (afx_hdl->peer_chan == P2P_INVALID_CHANNEL) + if (afx_hdl->peer_chan == P2P_INVALID_CHANSPEC) complete(&afx_hdl->act_frm_scan); return true; } @@ -1252,13 +1291,8 @@ bool brcmf_p2p_scan_finding_common_channel(struct brcmf_cfg80211_info *cfg, p2p_dev_addr, sizeof(p2p_dev_addr)); if ((err >= 0) && (ether_addr_equal(p2p_dev_addr, afx_hdl->tx_dst_addr))) { - if (!bi->ctl_ch) { - ch.chspec = le16_to_cpu(bi->chanspec); - cfg->d11inf.decchspec(&ch); - bi->ctl_ch = ch.control_ch_num; - } - afx_hdl->peer_chan = bi->ctl_ch; - brcmf_dbg(TRACE, "ACTION FRAME SCAN : Peer %pM found, channel : %d\n", + afx_hdl->peer_chan = le16_to_cpu(bi->chanspec); + brcmf_dbg(TRACE, "ACTION FRAME SCAN : Peer %pM found, chanspec : 0x%04x\n", afx_hdl->tx_dst_addr, afx_hdl->peer_chan); complete(&afx_hdl->act_frm_scan); } @@ -1281,6 +1315,10 @@ static s32 brcmf_p2p_abort_action_frame(struct brcmf_cfg80211_info *cfg) brcmf_dbg(TRACE, "Enter\n"); vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif; + + if (!vif) + vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif; + err = brcmf_fil_bsscfg_data_set(vif->ifp, "actframe_abort", &int_val, sizeof(s32)); if (err) @@ -1426,8 +1464,8 @@ int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp, if (test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status) && (ether_addr_equal(afx_hdl->tx_dst_addr, e->addr))) { - afx_hdl->peer_chan = ch.control_ch_num; - brcmf_dbg(INFO, "GON request: Peer found, channel=%d\n", + afx_hdl->peer_chan = be16_to_cpu(rxframe->chanspec); + brcmf_dbg(INFO, "GON request: Peer found, chanspec=0x%04x\n", afx_hdl->peer_chan); complete(&afx_hdl->act_frm_scan); } @@ -1470,9 +1508,7 @@ int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp, mgmt_frame_len += offsetof(struct ieee80211_mgmt, u); freq = ieee80211_channel_to_frequency(ch.control_ch_num, - ch.band == BRCMU_CHAN_BAND_2G ? - NL80211_BAND_2GHZ : - NL80211_BAND_5GHZ); + BRCMU_CHAN_BAND_TO_NL80211(ch.band)); wdev = &ifp->vif->wdev; cfg80211_rx_mgmt(wdev, freq, 0, (u8 *)mgmt_frame, mgmt_frame_len, 0); @@ -1531,6 +1567,7 @@ int brcmf_p2p_notify_action_tx_complete(struct brcmf_if *ifp, * * @p2p: p2p info struct for vif. * @af_params: action frame data/info. + * @vif: vif to send * * Send an action frame immediately without doing channel synchronization. * @@ -1539,12 +1576,17 @@ int brcmf_p2p_notify_action_tx_complete(struct brcmf_if *ifp, * frame is transmitted. */ static s32 brcmf_p2p_tx_action_frame(struct brcmf_p2p_info *p2p, - struct brcmf_fil_af_params_le *af_params) + struct brcmf_fil_af_params_le *af_params, + struct brcmf_cfg80211_vif *vif + ) { struct brcmf_pub *drvr = p2p->cfg->pub; - struct brcmf_cfg80211_vif *vif; - struct brcmf_p2p_action_frame *p2p_af; s32 err = 0; + struct brcmf_fil_action_frame_le *action_frame; + u16 action_frame_len; + + action_frame = &af_params->action_frame; + action_frame_len = le16_to_cpu(action_frame->len); brcmf_dbg(TRACE, "Enter\n"); @@ -1552,13 +1594,6 @@ static s32 brcmf_p2p_tx_action_frame(struct brcmf_p2p_info *p2p, clear_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status); clear_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status); - /* check if it is a p2p_presence response */ - p2p_af = (struct brcmf_p2p_action_frame *)af_params->action_frame.data; - if (p2p_af->subtype == P2P_AF_PRESENCE_RSP) - vif = p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif; - else - vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif; - err = brcmf_fil_bsscfg_data_set(vif->ifp, "actframe", af_params, sizeof(*af_params)); if (err) { @@ -1714,10 +1749,14 @@ static bool brcmf_p2p_check_dwell_overflow(u32 requested_dwell, * @cfg: driver private data for cfg80211 interface. * @ndev: net device to transmit on. * @af_params: configuration data for action frame. + * @vif: virtual interface to send */ bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, struct net_device *ndev, - struct brcmf_fil_af_params_le *af_params) + struct brcmf_fil_af_params_le *af_params, + struct brcmf_cfg80211_vif *vif, + struct ieee80211_channel *peer_listen_chan + ) { struct brcmf_p2p_info *p2p = &cfg->p2p; struct brcmf_if *ifp = netdev_priv(ndev); @@ -1725,6 +1764,7 @@ bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, struct brcmf_config_af_params config_af_params; struct afx_hdl *afx_hdl = &p2p->afx_hdl; struct brcmf_pub *drvr = cfg->pub; + struct brcmu_chan ch; u16 action_frame_len; bool ack = false; u8 category; @@ -1789,7 +1829,9 @@ bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, goto exit; } } else if (brcmf_p2p_is_p2p_action(action_frame->data, - action_frame_len)) { + action_frame_len) || + brcmf_p2p_is_dpp_pub_action(action_frame->data, + action_frame_len)) { /* do not configure anything. it will be */ /* sent with a default configuration */ } else { @@ -1826,12 +1868,13 @@ bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, /* validate channel and p2p ies */ if (config_af_params.search_channel && IS_P2P_SOCIAL_CHANNEL(le32_to_cpu(af_params->channel)) && + p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif && p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif->saved_ie.probe_req_ie_len) { afx_hdl = &p2p->afx_hdl; - afx_hdl->peer_listen_chan = le32_to_cpu(af_params->channel); + afx_hdl->peer_listen_chan = channel_to_chanspec(&cfg->d11inf, peer_listen_chan); if (brcmf_p2p_af_searching_channel(p2p) == - P2P_INVALID_CHANNEL) { + P2P_INVALID_CHANSPEC) { bphy_err(drvr, "Couldn't find peer's channel.\n"); goto exit; } @@ -1844,7 +1887,9 @@ bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, brcmf_notify_escan_complete(cfg, ifp, true, true); /* update channel */ - af_params->channel = cpu_to_le32(afx_hdl->peer_chan); + ch.chspec = afx_hdl->peer_chan; + cfg->d11inf.decchspec(&ch); + af_params->channel = cpu_to_le32(ch.control_ch_num); } dwell_jiffies = jiffies; dwell_overflow = brcmf_p2p_check_dwell_overflow(requested_dwell, @@ -1857,7 +1902,7 @@ bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, if (af_params->channel) msleep(P2P_AF_RETRY_DELAY_TIME); - ack = !brcmf_p2p_tx_action_frame(p2p, af_params); + ack = !brcmf_p2p_tx_action_frame(p2p, af_params, vif); tx_retry++; dwell_overflow = brcmf_p2p_check_dwell_overflow(requested_dwell, dwell_jiffies); @@ -1876,9 +1921,11 @@ bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, * not keep the dwell time, go to listen state again to get next action * response frame. */ + ch.chspec = afx_hdl->my_listen_chan; + cfg->d11inf.decchspec(&ch); if (ack && config_af_params.extra_listen && !p2p->block_gon_req_tx && test_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status) && - p2p->af_sent_channel == afx_hdl->my_listen_chan) { + p2p->af_sent_channel == ch.control_ch_num) { delta_ms = jiffies_to_msecs(jiffies - p2p->af_tx_sent_jiffies); if (le32_to_cpu(af_params->dwell_time) > delta_ms) extra_listen_time = le32_to_cpu(af_params->dwell_time) - @@ -1893,7 +1940,7 @@ bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, extra_listen_time); extra_listen_time += 100; if (!brcmf_p2p_discover_listen(p2p, - p2p->af_sent_channel, + afx_hdl->my_listen_chan, extra_listen_time)) { unsigned long duration; @@ -1958,8 +2005,8 @@ s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp, if (test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status) && (ether_addr_equal(afx_hdl->tx_dst_addr, e->addr))) { - afx_hdl->peer_chan = ch.control_ch_num; - brcmf_dbg(INFO, "PROBE REQUEST: Peer found, channel=%d\n", + afx_hdl->peer_chan = be16_to_cpu(rxframe->chanspec); + brcmf_dbg(INFO, "PROBE REQUEST: Peer found, chanspec=0x%04x\n", afx_hdl->peer_chan); complete(&afx_hdl->act_frm_scan); } @@ -1984,9 +2031,7 @@ s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp, mgmt_frame = (u8 *)(rxframe + 1); mgmt_frame_len = e->datalen - sizeof(*rxframe); freq = ieee80211_channel_to_frequency(ch.control_ch_num, - ch.band == BRCMU_CHAN_BAND_2G ? - NL80211_BAND_2GHZ : - NL80211_BAND_5GHZ); + BRCMU_CHAN_BAND_TO_NL80211(ch.band)); cfg80211_rx_mgmt(&vif->wdev, freq, 0, mgmt_frame, mgmt_frame_len, 0); @@ -2030,6 +2075,7 @@ static void brcmf_p2p_get_current_chanspec(struct brcmf_p2p_info *p2p, } } /* Use default channel for P2P */ + ch.band = BRCMU_CHAN_BAND_2G; ch.chnum = BRCMF_P2P_TEMP_CHAN; ch.bw = BRCMU_CHAN_BW_20; p2p->cfg->d11inf.encchspec(&ch); @@ -2424,8 +2470,12 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) brcmf_remove_interface(vif->ifp, true); brcmf_cfg80211_arm_vif_event(cfg, NULL); - if (iftype != NL80211_IFTYPE_P2P_DEVICE) - p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = NULL; + if (iftype != NL80211_IFTYPE_P2P_DEVICE) { + if (vif == p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif) + p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = NULL; + if (vif == p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION2].vif) + p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION2].vif = NULL; + } return err; } @@ -2504,6 +2554,7 @@ s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced) pri_ifp = brcmf_get_ifp(cfg->pub, 0); p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif = pri_ifp->vif; + init_completion(&p2p->send_af_done); if (p2pdev_forced) { err_ptr = brcmf_p2p_create_p2pdev(p2p, NULL, NULL); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h index d2ecee565bf2e..d71709aae7abc 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h @@ -87,7 +87,7 @@ struct afx_hdl { struct work_struct afx_work; struct completion act_frm_scan; bool is_active; - s32 peer_chan; + u16 peer_chan; bool is_listen; u16 my_listen_chan; u16 peer_listen_chan; @@ -138,6 +138,7 @@ struct brcmf_p2p_info { bool block_gon_req_tx; bool p2pdev_dynamically; bool wait_for_offchan_complete; + struct wireless_dev *remin_on_channel_wdev; }; s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced); @@ -170,7 +171,9 @@ int brcmf_p2p_notify_action_tx_complete(struct brcmf_if *ifp, void *data); bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, struct net_device *ndev, - struct brcmf_fil_af_params_le *af_params); + struct brcmf_fil_af_params_le *af_params, + struct brcmf_cfg80211_vif *vif, + struct ieee80211_channel *peer_listen_chan); bool brcmf_p2p_scan_finding_common_channel(struct brcmf_cfg80211_info *cfg, struct brcmf_bss_info_le *bi); s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp, diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index d8db0dbcfe091..30e3f6d6e66dc 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include @@ -38,6 +40,8 @@ #include "chip.h" #include "core.h" #include "common.h" +#include "cfg80211.h" +#include "trxhdr.h" enum brcmf_pcie_state { @@ -48,16 +52,19 @@ enum brcmf_pcie_state { BRCMF_FW_DEF(43602, "brcmfmac43602-pcie"); BRCMF_FW_DEF(4350, "brcmfmac4350-pcie"); BRCMF_FW_DEF(4350C, "brcmfmac4350c2-pcie"); -BRCMF_FW_DEF(4356, "brcmfmac4356-pcie"); -BRCMF_FW_DEF(43570, "brcmfmac43570-pcie"); +CY_FW_DEF(4356, "cyfmac4356-pcie"); +CY_FW_DEF(43570, "cyfmac43570-pcie"); BRCMF_FW_DEF(4358, "brcmfmac4358-pcie"); -BRCMF_FW_DEF(4359, "brcmfmac4359-pcie"); +CY_FW_DEF(4359, "cyfmac4359-pcie"); BRCMF_FW_DEF(4364, "brcmfmac4364-pcie"); BRCMF_FW_DEF(4365B, "brcmfmac4365b-pcie"); BRCMF_FW_DEF(4365C, "brcmfmac4365c-pcie"); BRCMF_FW_DEF(4366B, "brcmfmac4366b-pcie"); BRCMF_FW_DEF(4366C, "brcmfmac4366c-pcie"); BRCMF_FW_DEF(4371, "brcmfmac4371-pcie"); +CY_FW_DEF(4355, "cyfmac54591-pcie"); +CY_FW_TRXSE_DEF(55572, "cyfmac55572-pcie"); +CY_FW_DEF(4373, "cyfmac4373-pcie"); static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { BRCMF_FW_ENTRY(BRCM_CC_43602_CHIP_ID, 0xFFFFFFFF, 43602), @@ -78,8 +85,15 @@ static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { BRCMF_FW_ENTRY(BRCM_CC_4366_CHIP_ID, 0xFFFFFFF0, 4366C), BRCMF_FW_ENTRY(BRCM_CC_43664_CHIP_ID, 0xFFFFFFF0, 4366C), BRCMF_FW_ENTRY(BRCM_CC_4371_CHIP_ID, 0xFFFFFFFF, 4371), + BRCMF_FW_ENTRY(CY_CC_89459_CHIP_ID, 0xFFFFFFFF, 4355), + BRCMF_FW_ENTRY(CY_CC_55572_CHIP_ID, 0xFFFFFFFF, 55572), + BRCMF_FW_ENTRY(CY_CC_4373_CHIP_ID, 0xFFFFFFFF, 4373), }; +#define BRCMF_PCIE_REV_GE64(dev) (brcmf_chip_get_core((dev)->ci, \ + BCMA_CORE_PCIE2)->rev >= 64) + +#define BRCMF_PCIE_READ_SHARED_TIMEOUT 5000 /* msec */ #define BRCMF_PCIE_FW_UP_TIMEOUT 5000 /* msec */ #define BRCMF_PCIE_REG_MAP_SIZE (32 * 1024) @@ -90,7 +104,8 @@ static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { #define BRCMF_PCIE_BAR0_WRAPPERBASE 0x70 #define BRCMF_PCIE_BAR0_WRAPBASE_DMP_OFFSET 0x1000 -#define BRCMF_PCIE_BARO_PCIE_ENUM_OFFSET 0x2000 +#define BRCMF_PCIE_BAR0_PCIE_ENUM_OFFSET 0x2000 +#define BRCMF_CYW55572_PCIE_BAR0_PCIE_ENUM_OFFSET 0x3000 #define BRCMF_PCIE_ARMCR4REG_BANKIDX 0x40 #define BRCMF_PCIE_ARMCR4REG_BANKPDA 0x4C @@ -102,12 +117,16 @@ static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { #define BRCMF_PCIE_REG_LINK_STATUS_CTRL 0xBC #define BRCMF_PCIE_PCIE2REG_INTMASK 0x24 -#define BRCMF_PCIE_PCIE2REG_MAILBOXINT 0x48 -#define BRCMF_PCIE_PCIE2REG_MAILBOXMASK 0x4C +#define BRCMF_PCIE_PCIE2REG_MAILBOXINT(dev) (BRCMF_PCIE_REV_GE64(dev) ? \ + 0xC30 : 0x48) +#define BRCMF_PCIE_PCIE2REG_MAILBOXMASK(dev) (BRCMF_PCIE_REV_GE64(dev) ? \ + 0xC34 : 0x4C) #define BRCMF_PCIE_PCIE2REG_CONFIGADDR 0x120 #define BRCMF_PCIE_PCIE2REG_CONFIGDATA 0x124 #define BRCMF_PCIE_PCIE2REG_H2D_MAILBOX_0 0x140 #define BRCMF_PCIE_PCIE2REG_H2D_MAILBOX_1 0x144 +#define BRCMF_PCIE_PCIE2REG_DAR_D2H_MSG_0 0xA80 +#define BRCMF_PCIE_PCIE2REG_DAR_H2D_MSG_0 0xA90 #define BRCMF_PCIE2_INTA 0x01 #define BRCMF_PCIE2_INTB 0x02 @@ -119,30 +138,69 @@ static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { #define BRCMF_PCIE_MB_INT_FN0_0 0x0100 #define BRCMF_PCIE_MB_INT_FN0_1 0x0200 -#define BRCMF_PCIE_MB_INT_D2H0_DB0 0x10000 -#define BRCMF_PCIE_MB_INT_D2H0_DB1 0x20000 -#define BRCMF_PCIE_MB_INT_D2H1_DB0 0x40000 -#define BRCMF_PCIE_MB_INT_D2H1_DB1 0x80000 -#define BRCMF_PCIE_MB_INT_D2H2_DB0 0x100000 -#define BRCMF_PCIE_MB_INT_D2H2_DB1 0x200000 -#define BRCMF_PCIE_MB_INT_D2H3_DB0 0x400000 -#define BRCMF_PCIE_MB_INT_D2H3_DB1 0x800000 - -#define BRCMF_PCIE_MB_INT_D2H_DB (BRCMF_PCIE_MB_INT_D2H0_DB0 | \ - BRCMF_PCIE_MB_INT_D2H0_DB1 | \ - BRCMF_PCIE_MB_INT_D2H1_DB0 | \ - BRCMF_PCIE_MB_INT_D2H1_DB1 | \ - BRCMF_PCIE_MB_INT_D2H2_DB0 | \ - BRCMF_PCIE_MB_INT_D2H2_DB1 | \ - BRCMF_PCIE_MB_INT_D2H3_DB0 | \ - BRCMF_PCIE_MB_INT_D2H3_DB1) - +#define BRCMF_PCIE_MB_INT_D2H0_DB0 0x10000 +#define BRCMF_PCIE_MB_INT_D2H0_DB1 0x20000 +#define BRCMF_PCIE_MB_INT_D2H1_DB0 0x40000 +#define BRCMF_PCIE_MB_INT_D2H1_DB1 0x80000 +#define BRCMF_PCIE_MB_INT_D2H2_DB0 0x100000 +#define BRCMF_PCIE_MB_INT_D2H2_DB1 0x200000 +#define BRCMF_PCIE_MB_INT_D2H3_DB0 0x400000 +#define BRCMF_PCIE_MB_INT_D2H3_DB1 0x800000 +#define BRCMF_PCIE_MB_INT_D2H_DB_ALL (BRCMF_PCIE_MB_INT_D2H0_DB0 | \ + BRCMF_PCIE_MB_INT_D2H0_DB1 | \ + BRCMF_PCIE_MB_INT_D2H1_DB0 | \ + BRCMF_PCIE_MB_INT_D2H1_DB1 | \ + BRCMF_PCIE_MB_INT_D2H2_DB0 | \ + BRCMF_PCIE_MB_INT_D2H2_DB1 | \ + BRCMF_PCIE_MB_INT_D2H3_DB0 | \ + BRCMF_PCIE_MB_INT_D2H3_DB1) + +#define BRCMF_PCIE_MB_INT_D2H0_DB0_GE64 0x0001 +#define BRCMF_PCIE_MB_INT_D2H0_DB1_GE64 0x0002 +#define BRCMF_PCIE_MB_INT_D2H1_DB0_GE64 0x0004 +#define BRCMF_PCIE_MB_INT_D2H1_DB1_GE64 0x0008 +#define BRCMF_PCIE_MB_INT_D2H2_DB0_GE64 0x0010 +#define BRCMF_PCIE_MB_INT_D2H2_DB1_GE64 0x0020 +#define BRCMF_PCIE_MB_INT_D2H3_DB0_GE64 0x0040 +#define BRCMF_PCIE_MB_INT_D2H3_DB1_GE64 0x0080 +#define BRCMF_PCIE_MB_INT_D2H4_DB0_GE64 0x0100 +#define BRCMF_PCIE_MB_INT_D2H4_DB1_GE64 0x0200 +#define BRCMF_PCIE_MB_INT_D2H5_DB0_GE64 0x0400 +#define BRCMF_PCIE_MB_INT_D2H5_DB1_GE64 0x0800 +#define BRCMF_PCIE_MB_INT_D2H6_DB0_GE64 0x1000 +#define BRCMF_PCIE_MB_INT_D2H6_DB1_GE64 0x2000 +#define BRCMF_PCIE_MB_INT_D2H7_DB0_GE64 0x4000 +#define BRCMF_PCIE_MB_INT_D2H7_DB1_GE64 0x8000 +#define BRCMF_PCIE_MB_INT_D2H_DB_ALL_GE64 \ + (BRCMF_PCIE_MB_INT_D2H0_DB0_GE64 | \ + BRCMF_PCIE_MB_INT_D2H0_DB1_GE64 | \ + BRCMF_PCIE_MB_INT_D2H1_DB0_GE64 | \ + BRCMF_PCIE_MB_INT_D2H1_DB1_GE64 | \ + BRCMF_PCIE_MB_INT_D2H2_DB0_GE64 | \ + BRCMF_PCIE_MB_INT_D2H2_DB1_GE64 | \ + BRCMF_PCIE_MB_INT_D2H3_DB0_GE64 | \ + BRCMF_PCIE_MB_INT_D2H3_DB1_GE64 | \ + BRCMF_PCIE_MB_INT_D2H4_DB0_GE64 | \ + BRCMF_PCIE_MB_INT_D2H4_DB1_GE64 | \ + BRCMF_PCIE_MB_INT_D2H5_DB0_GE64 | \ + BRCMF_PCIE_MB_INT_D2H5_DB1_GE64 | \ + BRCMF_PCIE_MB_INT_D2H6_DB0_GE64 | \ + BRCMF_PCIE_MB_INT_D2H6_DB1_GE64 | \ + BRCMF_PCIE_MB_INT_D2H7_DB0_GE64 | \ + BRCMF_PCIE_MB_INT_D2H7_DB1_GE64) + +#define BRCMF_PCIE_MB_INT_D2H_DB(dev) (BRCMF_PCIE_REV_GE64(dev) ? \ + BRCMF_PCIE_MB_INT_D2H_DB_ALL_GE64 : \ + BRCMF_PCIE_MB_INT_D2H_DB_ALL) + +#define BRCMF_PCIE_SHARED_VERSION_6 6 #define BRCMF_PCIE_SHARED_VERSION_7 7 #define BRCMF_PCIE_MIN_SHARED_VERSION 5 #define BRCMF_PCIE_MAX_SHARED_VERSION BRCMF_PCIE_SHARED_VERSION_7 #define BRCMF_PCIE_SHARED_VERSION_MASK 0x00FF #define BRCMF_PCIE_SHARED_DMA_INDEX 0x10000 #define BRCMF_PCIE_SHARED_DMA_2B_IDX 0x100000 +#define BRCMF_PCIE_SHARED_USE_MAILBOX 0x2000000 #define BRCMF_PCIE_SHARED_HOSTRDY_DB1 0x10000000 #define BRCMF_PCIE_FLAGS_HTOD_SPLIT 0x4000 @@ -159,6 +217,7 @@ static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { #define BRCMF_SHARED_DMA_SCRATCH_ADDR_OFFSET 56 #define BRCMF_SHARED_DMA_RINGUPD_LEN_OFFSET 64 #define BRCMF_SHARED_DMA_RINGUPD_ADDR_OFFSET 68 +#define BRCMF_SHARED_HOST_CAP_OFFSET 84 #define BRCMF_RING_H2D_RING_COUNT_OFFSET 0 #define BRCMF_RING_D2H_RING_COUNT_OFFSET 1 @@ -173,6 +232,9 @@ static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { #define BRCMF_DEF_MAX_RXBUFPOST 255 +#define BRCMF_HOSTCAP_H2D_ENABLE_HOSTRDY 0x400 +#define BRCMF_HOSTCAP_DS_NO_OOB_DW 0x1000 + #define BRCMF_CONSOLE_BUFADDR_OFFSET 8 #define BRCMF_CONSOLE_BUFSIZE_OFFSET 12 #define BRCMF_CONSOLE_WRITEIDX_OFFSET 16 @@ -198,18 +260,32 @@ static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { #define BRCMF_PCIE_CFGREG_MSI_ADDR_L 0x5C #define BRCMF_PCIE_CFGREG_MSI_ADDR_H 0x60 #define BRCMF_PCIE_CFGREG_MSI_DATA 0x64 +#define BRCMF_PCIE_CFGREG_REVID 0x6C #define BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL 0xBC #define BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL2 0xDC #define BRCMF_PCIE_CFGREG_RBAR_CTRL 0x228 #define BRCMF_PCIE_CFGREG_PML1_SUB_CTRL1 0x248 #define BRCMF_PCIE_CFGREG_REG_BAR2_CONFIG 0x4E0 #define BRCMF_PCIE_CFGREG_REG_BAR3_CONFIG 0x4F4 +#define BRCMF_PCIE_CFGREG_REVID_SECURE_MODE BIT(31) #define BRCMF_PCIE_LINK_STATUS_CTRL_ASPM_ENAB 3 /* Magic number at a magic location to find RAM size */ #define BRCMF_RAMSIZE_MAGIC 0x534d4152 /* SMAR */ #define BRCMF_RAMSIZE_OFFSET 0x6c +#define BRCMF_ENTROPY_SEED_LEN 64u +#define BRCMF_ENTROPY_NONCE_LEN 16u +#define BRCMF_ENTROPY_HOST_LEN (BRCMF_ENTROPY_SEED_LEN + \ + BRCMF_ENTROPY_NONCE_LEN) +#define BRCMF_NVRAM_OFFSET_TCM 4u +#define BRCMF_NVRAM_COMPRS_FACTOR 4u +#define BRCMF_NVRAM_RNG_SIGNATURE 0xFEEDC0DEu + +struct brcmf_rand_metadata { + u32 signature; + u32 count; +}; struct brcmf_pcie_console { u32 base_addr; @@ -259,6 +335,9 @@ struct brcmf_pciedev_info { struct brcmf_chip *ci; u32 coreid; struct brcmf_pcie_shared_info shared; + u8 hostready; + bool use_mailbox; + bool use_d0_inform; wait_queue_head_t mbdata_resp_wait; bool mbdata_completed; bool irq_allocated; @@ -271,6 +350,14 @@ struct brcmf_pciedev_info { void (*write_ptr)(struct brcmf_pciedev_info *devinfo, u32 mem_offset, u16 value); struct brcmf_mp_device *settings; +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + ulong bar1_size; +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ +#ifdef DEBUG + u32 console_interval; + bool console_active; + struct timer_list timer; +#endif }; struct brcmf_pcie_ringbuf { @@ -341,6 +428,17 @@ static void brcmf_pcie_setup(struct device *dev, int ret, struct brcmf_fw_request *fwreq); static struct brcmf_fw_request * brcmf_pcie_prepare_fw_request(struct brcmf_pciedev_info *devinfo); +static void brcmf_pcie_bus_console_init(struct brcmf_pciedev_info *devinfo); +static void brcmf_pcie_bus_console_read(struct brcmf_pciedev_info *devinfo, + bool error); + +static void +brcmf_pcie_fwcon_timer(struct brcmf_pciedev_info *devinfo, bool active); +static void brcmf_pcie_debugfs_create(struct device *dev); + +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ +DEFINE_RAW_SPINLOCK(pcie_lock); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ static u32 brcmf_pcie_read_reg32(struct brcmf_pciedev_info *devinfo, u32 reg_offset) @@ -365,8 +463,24 @@ static u8 brcmf_pcie_read_tcm8(struct brcmf_pciedev_info *devinfo, u32 mem_offset) { void __iomem *address = devinfo->tcm + mem_offset; +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + unsigned long flags; + u8 value; + + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= devinfo->bar1_size) { + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - devinfo->bar1_size; + } + value = ioread8(address); + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, 0x0); + raw_spin_unlock_irqrestore(&pcie_lock, flags); + return value; +#else return (ioread8(address)); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ } @@ -374,8 +488,24 @@ static u16 brcmf_pcie_read_tcm16(struct brcmf_pciedev_info *devinfo, u32 mem_offset) { void __iomem *address = devinfo->tcm + mem_offset; +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + u16 value; + unsigned long flags; + + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= devinfo->bar1_size) { + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - devinfo->bar1_size; + } + value = ioread16(address); + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, 0x0); + raw_spin_unlock_irqrestore(&pcie_lock, flags); + return value; +#else return (ioread16(address)); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ } @@ -384,8 +514,22 @@ brcmf_pcie_write_tcm16(struct brcmf_pciedev_info *devinfo, u32 mem_offset, u16 value) { void __iomem *address = devinfo->tcm + mem_offset; +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + unsigned long flags; + + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= devinfo->bar1_size) { + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - devinfo->bar1_size; + } iowrite16(value, address); + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, 0x0); + raw_spin_unlock_irqrestore(&pcie_lock, flags); +#else + iowrite16(value, address); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ } @@ -412,8 +556,24 @@ static u32 brcmf_pcie_read_tcm32(struct brcmf_pciedev_info *devinfo, u32 mem_offset) { void __iomem *address = devinfo->tcm + mem_offset; +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + u32 value; + unsigned long flags; + + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= devinfo->bar1_size) { + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - devinfo->bar1_size; + } + value = ioread32(address); + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, 0x0); + raw_spin_unlock_irqrestore(&pcie_lock, flags); + return value; +#else return (ioread32(address)); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ } @@ -422,17 +582,47 @@ brcmf_pcie_write_tcm32(struct brcmf_pciedev_info *devinfo, u32 mem_offset, u32 value) { void __iomem *address = devinfo->tcm + mem_offset; - +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + unsigned long flags; + + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= devinfo->bar1_size) { + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - devinfo->bar1_size; + } + iowrite32(value, address); + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, 0x0); + raw_spin_unlock_irqrestore(&pcie_lock, flags); +#else iowrite32(value, address); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ } static u32 brcmf_pcie_read_ram32(struct brcmf_pciedev_info *devinfo, u32 mem_offset) { - void __iomem *addr = devinfo->tcm + devinfo->ci->rambase + mem_offset; + void __iomem *address = devinfo->tcm + devinfo->ci->rambase + + mem_offset; +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + u32 value; + unsigned long flags; + + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= devinfo->bar1_size) { + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - devinfo->bar1_size; + } + value = ioread32(address); + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, 0x0); + raw_spin_unlock_irqrestore(&pcie_lock, flags); - return (ioread32(addr)); + return value; +#else + return (ioread32(address)); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ } @@ -440,9 +630,23 @@ static void brcmf_pcie_write_ram32(struct brcmf_pciedev_info *devinfo, u32 mem_offset, u32 value) { - void __iomem *addr = devinfo->tcm + devinfo->ci->rambase + mem_offset; + void __iomem *address = devinfo->tcm + devinfo->ci->rambase + + mem_offset; +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + unsigned long flags; - iowrite32(value, addr); + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= devinfo->bar1_size) { + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - devinfo->bar1_size; + } + iowrite32(value, address); + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, 0x0); + raw_spin_unlock_irqrestore(&pcie_lock, flags); +#else + iowrite32(value, address); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ } @@ -454,12 +658,30 @@ brcmf_pcie_copy_mem_todev(struct brcmf_pciedev_info *devinfo, u32 mem_offset, __le32 *src32; __le16 *src16; u8 *src8; +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + unsigned long flags; +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ if (((ulong)address & 4) || ((ulong)srcaddr & 4) || (len & 4)) { if (((ulong)address & 2) || ((ulong)srcaddr & 2) || (len & 2)) { src8 = (u8 *)srcaddr; while (len) { +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= + devinfo->bar1_size) { + pci_write_config_dword + (devinfo->pdev, + BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - + devinfo->bar1_size; + } +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ iowrite8(*src8, address); +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_unlock_irqrestore(&pcie_lock, flags); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ address++; src8++; len--; @@ -468,7 +690,22 @@ brcmf_pcie_copy_mem_todev(struct brcmf_pciedev_info *devinfo, u32 mem_offset, len = len / 2; src16 = (__le16 *)srcaddr; while (len) { +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= + devinfo->bar1_size) { + pci_write_config_dword + (devinfo->pdev, + BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - + devinfo->bar1_size; + } +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ iowrite16(le16_to_cpu(*src16), address); +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_unlock_irqrestore(&pcie_lock, flags); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ address += 2; src16++; len--; @@ -478,12 +715,29 @@ brcmf_pcie_copy_mem_todev(struct brcmf_pciedev_info *devinfo, u32 mem_offset, len = len / 4; src32 = (__le32 *)srcaddr; while (len) { +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= + devinfo->bar1_size) { + pci_write_config_dword + (devinfo->pdev, + BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - devinfo->bar1_size; + } +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ iowrite32(le32_to_cpu(*src32), address); +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_unlock_irqrestore(&pcie_lock, flags); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ address += 4; src32++; len--; } } +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, 0x0); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ } @@ -495,12 +749,30 @@ brcmf_pcie_copy_dev_tomem(struct brcmf_pciedev_info *devinfo, u32 mem_offset, __le32 *dst32; __le16 *dst16; u8 *dst8; +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + unsigned long flags; +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ if (((ulong)address & 4) || ((ulong)dstaddr & 4) || (len & 4)) { if (((ulong)address & 2) || ((ulong)dstaddr & 2) || (len & 2)) { dst8 = (u8 *)dstaddr; while (len) { +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= + devinfo->bar1_size) { + pci_write_config_dword + (devinfo->pdev, + BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - + devinfo->bar1_size; + } +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ *dst8 = ioread8(address); +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_unlock_irqrestore(&pcie_lock, flags); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ address++; dst8++; len--; @@ -509,7 +781,22 @@ brcmf_pcie_copy_dev_tomem(struct brcmf_pciedev_info *devinfo, u32 mem_offset, len = len / 2; dst16 = (__le16 *)dstaddr; while (len) { +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= + devinfo->bar1_size) { + pci_write_config_dword + (devinfo->pdev, + BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - + devinfo->bar1_size; + } +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ *dst16 = cpu_to_le16(ioread16(address)); +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_unlock_irqrestore(&pcie_lock, flags); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ address += 2; dst16++; len--; @@ -519,12 +806,29 @@ brcmf_pcie_copy_dev_tomem(struct brcmf_pciedev_info *devinfo, u32 mem_offset, len = len / 4; dst32 = (__le32 *)dstaddr; while (len) { +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= + devinfo->bar1_size) { + pci_write_config_dword + (devinfo->pdev, + BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - devinfo->bar1_size; + } +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ *dst32 = cpu_to_le32(ioread32(address)); +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_unlock_irqrestore(&pcie_lock, flags); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ address += 4; dst32++; len--; } } +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, 0x0); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ } @@ -568,6 +872,9 @@ static void brcmf_pcie_reset_device(struct brcmf_pciedev_info *devinfo) BRCMF_PCIE_CFGREG_MSI_ADDR_L, BRCMF_PCIE_CFGREG_MSI_ADDR_H, BRCMF_PCIE_CFGREG_MSI_DATA, +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + BCMA_PCI_BAR1_WIN, +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL2, BRCMF_PCIE_CFGREG_RBAR_CTRL, BRCMF_PCIE_CFGREG_PML1_SUB_CTRL1, @@ -589,9 +896,15 @@ static void brcmf_pcie_reset_device(struct brcmf_pciedev_info *devinfo) val); /* Watchdog reset */ + if (devinfo->ci->blhs) + devinfo->ci->blhs->init(devinfo->ci); brcmf_pcie_select_core(devinfo, BCMA_CORE_CHIPCOMMON); WRITECC32(devinfo, watchdog, 4); msleep(100); + if (devinfo->ci->blhs) + if (devinfo->ci->blhs->post_wdreset(devinfo->ci)) + return; + /* Restore ASPM */ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); @@ -630,8 +943,39 @@ static void brcmf_pcie_attach(struct brcmf_pciedev_info *devinfo) } +static int brcmf_pcie_bus_readshared(struct brcmf_pciedev_info *devinfo, + u32 nvram_csm) +{ + struct brcmf_bus *bus = dev_get_drvdata(&devinfo->pdev->dev); + u32 loop_counter; + u32 addr_le; + u32 addr = 0; + + loop_counter = BRCMF_PCIE_READ_SHARED_TIMEOUT / 50; + while ((addr == 0 || addr == nvram_csm) && (loop_counter)) { + msleep(50); + addr_le = brcmf_pcie_read_ram32(devinfo, + devinfo->ci->ramsize - 4); + addr = le32_to_cpu(addr_le); + loop_counter--; + } + if (addr == 0 || addr == nvram_csm || addr < devinfo->ci->rambase || + addr >= devinfo->ci->rambase + devinfo->ci->ramsize) { + brcmf_err(bus, "Invalid shared RAM address 0x%08x\n", addr); + return -ENODEV; + } + devinfo->shared.tcm_base_address = addr; + brcmf_dbg(PCIE, "Shared RAM addr: 0x%08x\n", addr); + + brcmf_pcie_bus_console_init(devinfo); + return 0; +} + static int brcmf_pcie_enter_download_state(struct brcmf_pciedev_info *devinfo) { + struct brcmf_bus *bus = dev_get_drvdata(&devinfo->pdev->dev); + int err = 0; + if (devinfo->ci->chip == BRCM_CC_43602_CHIP_ID) { brcmf_pcie_select_core(devinfo, BCMA_CORE_ARM_CR4); brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKIDX, @@ -643,7 +987,19 @@ static int brcmf_pcie_enter_download_state(struct brcmf_pciedev_info *devinfo) brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKPDA, 0); } - return 0; + + if (devinfo->ci->blhs) { + err = devinfo->ci->blhs->prep_fwdl(devinfo->ci); + if (err) { + brcmf_err(bus, "FW download preparation failed"); + return err; + } + + if (!brcmf_pcie_bus_readshared(devinfo, 0)) + brcmf_pcie_bus_console_read(devinfo, false); + } + + return err; } @@ -657,8 +1013,14 @@ static int brcmf_pcie_exit_download_state(struct brcmf_pciedev_info *devinfo, brcmf_chip_resetcore(core, 0, 0, 0); } - if (!brcmf_chip_set_active(devinfo->ci, resetintr)) - return -EINVAL; + if (devinfo->ci->blhs) { + brcmf_pcie_bus_console_read(devinfo, false); + devinfo->ci->blhs->post_nvramdl(devinfo->ci); + } else { + if (!brcmf_chip_set_active(devinfo->ci, resetintr)) + return -EINVAL; + } + return 0; } @@ -667,41 +1029,53 @@ static int brcmf_pcie_send_mb_data(struct brcmf_pciedev_info *devinfo, u32 htod_mb_data) { struct brcmf_pcie_shared_info *shared; + struct brcmf_bus *bus; + int err; struct brcmf_core *core; u32 addr; u32 cur_htod_mb_data; u32 i; shared = &devinfo->shared; - addr = shared->htod_mb_data_addr; - cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr); - - if (cur_htod_mb_data != 0) - brcmf_dbg(PCIE, "MB transaction is already pending 0x%04x\n", - cur_htod_mb_data); - - i = 0; - while (cur_htod_mb_data != 0) { - msleep(10); - i++; - if (i > 100) - return -EIO; + bus = dev_get_drvdata(&devinfo->pdev->dev); + if (shared->version >= BRCMF_PCIE_SHARED_VERSION_6 && + !devinfo->use_mailbox) { + err = brcmf_msgbuf_tx_mbdata(bus->drvr, htod_mb_data); + if (err) { + brcmf_err(bus, "sendimg mbdata failed err=%d\n", err); + return err; + } + } else { + addr = shared->htod_mb_data_addr; cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr); - } - brcmf_pcie_write_tcm32(devinfo, addr, htod_mb_data); - pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1); + if (cur_htod_mb_data != 0) + brcmf_dbg(PCIE, "MB transaction is already pending 0x%04x\n", + cur_htod_mb_data); + + i = 0; + while (cur_htod_mb_data != 0) { + msleep(10); + i++; + if (i > 100) + return -EIO; + cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr); + } - /* Send mailbox interrupt twice as a hardware workaround */ - core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_PCIE2); - if (core->rev <= 13) + brcmf_pcie_write_tcm32(devinfo, addr, htod_mb_data); pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1); + /* Send mailbox interrupt twice as a hardware workaround */ + core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_PCIE2); + if (core->rev <= 13) + pci_write_config_dword(devinfo->pdev, + BRCMF_PCIE_REG_SBMBX, 1); + } return 0; } -static void brcmf_pcie_handle_mb_data(struct brcmf_pciedev_info *devinfo) +static u32 brcmf_pcie_read_mb_data(struct brcmf_pciedev_info *devinfo) { struct brcmf_pcie_shared_info *shared; u32 addr; @@ -710,36 +1084,42 @@ static void brcmf_pcie_handle_mb_data(struct brcmf_pciedev_info *devinfo) shared = &devinfo->shared; addr = shared->dtoh_mb_data_addr; dtoh_mb_data = brcmf_pcie_read_tcm32(devinfo, addr); + brcmf_pcie_write_tcm32(devinfo, addr, 0); + return dtoh_mb_data; +} - if (!dtoh_mb_data) - return; +void brcmf_pcie_handle_mb_data(struct brcmf_bus *bus_if, u32 d2h_mb_data) +{ + struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie; + struct brcmf_pciedev_info *devinfo = buspub->devinfo; - brcmf_pcie_write_tcm32(devinfo, addr, 0); + brcmf_dbg(INFO, "D2H_MB_DATA: 0x%04x\n", d2h_mb_data); - brcmf_dbg(PCIE, "D2H_MB_DATA: 0x%04x\n", dtoh_mb_data); - if (dtoh_mb_data & BRCMF_D2H_DEV_DS_ENTER_REQ) { - brcmf_dbg(PCIE, "D2H_MB_DATA: DEEP SLEEP REQ\n"); + if (d2h_mb_data & BRCMF_D2H_DEV_DS_ENTER_REQ) { + brcmf_dbg(INFO, "D2H_MB_DATA: DEEP SLEEP REQ\n"); brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_DS_ACK); - brcmf_dbg(PCIE, "D2H_MB_DATA: sent DEEP SLEEP ACK\n"); + brcmf_dbg(INFO, "D2H_MB_DATA: sent DEEP SLEEP ACK\n"); } - if (dtoh_mb_data & BRCMF_D2H_DEV_DS_EXIT_NOTE) - brcmf_dbg(PCIE, "D2H_MB_DATA: DEEP SLEEP EXIT\n"); - if (dtoh_mb_data & BRCMF_D2H_DEV_D3_ACK) { - brcmf_dbg(PCIE, "D2H_MB_DATA: D3 ACK\n"); + + if (d2h_mb_data & BRCMF_D2H_DEV_DS_EXIT_NOTE) + brcmf_dbg(INFO, "D2H_MB_DATA: DEEP SLEEP EXIT\n"); + if (d2h_mb_data & BRCMF_D2H_DEV_D3_ACK) { + brcmf_dbg(INFO, "D2H_MB_DATA: D3 ACK\n"); devinfo->mbdata_completed = true; wake_up(&devinfo->mbdata_resp_wait); } - if (dtoh_mb_data & BRCMF_D2H_DEV_FWHALT) { - brcmf_dbg(PCIE, "D2H_MB_DATA: FW HALT\n"); + + if (d2h_mb_data & BRCMF_D2H_DEV_FWHALT) { + brcmf_dbg(INFO, "D2H_MB_DATA: FW HALT\n"); brcmf_fw_crashed(&devinfo->pdev->dev); } } - static void brcmf_pcie_bus_console_init(struct brcmf_pciedev_info *devinfo) { struct brcmf_pcie_shared_info *shared; struct brcmf_pcie_console *console; + u32 buf_addr; u32 addr; shared = &devinfo->shared; @@ -748,7 +1128,12 @@ static void brcmf_pcie_bus_console_init(struct brcmf_pciedev_info *devinfo) console->base_addr = brcmf_pcie_read_tcm32(devinfo, addr); addr = console->base_addr + BRCMF_CONSOLE_BUFADDR_OFFSET; - console->buf_addr = brcmf_pcie_read_tcm32(devinfo, addr); + buf_addr = brcmf_pcie_read_tcm32(devinfo, addr); + /* reset console index when buffer address is updated */ + if (console->buf_addr != buf_addr) { + console->buf_addr = buf_addr; + console->read_idx = 0; + } addr = console->base_addr + BRCMF_CONSOLE_BUFSIZE_OFFSET; console->bufsize = brcmf_pcie_read_tcm32(devinfo, addr); @@ -808,14 +1193,16 @@ static void brcmf_pcie_bus_console_read(struct brcmf_pciedev_info *devinfo, static void brcmf_pcie_intr_disable(struct brcmf_pciedev_info *devinfo) { - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK, 0); + brcmf_pcie_write_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_MAILBOXMASK(devinfo), 0); } static void brcmf_pcie_intr_enable(struct brcmf_pciedev_info *devinfo) { - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK, - BRCMF_PCIE_MB_INT_D2H_DB | + brcmf_pcie_write_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_MAILBOXMASK(devinfo), + BRCMF_PCIE_MB_INT_D2H_DB(devinfo) | BRCMF_PCIE_MB_INT_FN0_0 | BRCMF_PCIE_MB_INT_FN0_1); } @@ -831,7 +1218,8 @@ static irqreturn_t brcmf_pcie_quick_check_isr(int irq, void *arg) { struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg; - if (brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT)) { + if (brcmf_pcie_read_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_MAILBOXINT(devinfo))) { brcmf_pcie_intr_disable(devinfo); brcmf_dbg(PCIE, "Enter\n"); return IRQ_WAKE_THREAD; @@ -844,17 +1232,25 @@ static irqreturn_t brcmf_pcie_isr_thread(int irq, void *arg) { struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg; u32 status; + u32 d2h_mbdata; + struct pci_dev *pdev = devinfo->pdev; + struct brcmf_bus *bus = dev_get_drvdata(&pdev->dev); devinfo->in_irq = true; - status = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT); + status = brcmf_pcie_read_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_MAILBOXINT(devinfo)); brcmf_dbg(PCIE, "Enter %x\n", status); if (status) { - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, + brcmf_pcie_write_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_MAILBOXINT(devinfo), status); if (status & (BRCMF_PCIE_MB_INT_FN0_0 | - BRCMF_PCIE_MB_INT_FN0_1)) - brcmf_pcie_handle_mb_data(devinfo); - if (status & BRCMF_PCIE_MB_INT_D2H_DB) { + BRCMF_PCIE_MB_INT_FN0_1)) { + d2h_mbdata = brcmf_pcie_read_mb_data(devinfo); + brcmf_pcie_handle_mb_data(bus, d2h_mbdata); + } + + if (status & BRCMF_PCIE_MB_INT_D2H_DB(devinfo)) { if (devinfo->state == BRCMFMAC_PCIE_STATE_UP) brcmf_proto_msgbuf_rx_trigger( &devinfo->pdev->dev); @@ -913,8 +1309,11 @@ static void brcmf_pcie_release_irq(struct brcmf_pciedev_info *devinfo) if (devinfo->in_irq) brcmf_err(bus, "Still in IRQ (processing) !!!\n"); - status = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT); - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, status); + status = brcmf_pcie_read_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_MAILBOXINT(devinfo)); + brcmf_pcie_write_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_MAILBOXINT(devinfo), + status); devinfo->irq_allocated = false; } @@ -1136,9 +1535,14 @@ static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo) u16 max_flowrings; u16 max_submissionrings; u16 max_completionrings; - +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + brcmf_pcie_copy_dev_tomem(devinfo, devinfo->shared.ring_info_addr, + &ringinfo, sizeof(ringinfo)); +#else memcpy_fromio(&ringinfo, devinfo->tcm + devinfo->shared.ring_info_addr, sizeof(ringinfo)); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ + if (devinfo->shared.version >= 6) { max_submissionrings = le16_to_cpu(ringinfo.max_submissionrings); max_flowrings = le16_to_cpu(ringinfo.max_flowrings); @@ -1149,6 +1553,10 @@ static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo) BRCMF_NROF_H2D_COMMON_MSGRINGS; max_completionrings = BRCMF_NROF_D2H_COMMON_MSGRINGS; } + if (max_flowrings > 256) { + brcmf_err(bus, "invalid max_flowrings(%d)\n", max_flowrings); + return -EIO; + } if (devinfo->dma_idx_sz != 0) { bufsz = (max_submissionrings + max_completionrings) * @@ -1207,8 +1615,14 @@ static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo) ringinfo.d2h_r_idx_hostaddr.high_addr = cpu_to_le32(address >> 32); +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + brcmf_pcie_copy_mem_todev(devinfo, + devinfo->shared.ring_info_addr, + &ringinfo, sizeof(ringinfo)); +#else memcpy_toio(devinfo->tcm + devinfo->shared.ring_info_addr, &ringinfo, sizeof(ringinfo)); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ brcmf_dbg(PCIE, "Using host memory indices\n"); } @@ -1344,6 +1758,11 @@ static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo) static void brcmf_pcie_down(struct device *dev) { + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pciedev *pcie_bus_dev = bus_if->bus_priv.pcie; + struct brcmf_pciedev_info *devinfo = pcie_bus_dev->devinfo; + + brcmf_pcie_fwcon_timer(devinfo, false); } @@ -1407,8 +1826,10 @@ int brcmf_pcie_get_fwname(struct device *dev, const char *ext, u8 *fw_name) struct brcmf_fw_name fwnames[] = { { ext, fw_name }, }; + u32 chip; - fwreq = brcmf_fw_alloc_request(bus_if->chip, bus_if->chiprev, + chip = bus_if->chip; + fwreq = brcmf_fw_alloc_request(chip, bus_if->chiprev, brcmf_pcie_fwnames, ARRAY_SIZE(brcmf_pcie_fwnames), fwnames, ARRAY_SIZE(fwnames)); @@ -1463,6 +1884,7 @@ static const struct brcmf_bus_ops brcmf_pcie_bus_ops = { .get_memdump = brcmf_pcie_get_memdump, .get_fwname = brcmf_pcie_get_fwname, .reset = brcmf_pcie_reset, + .debugfs_create = brcmf_pcie_debugfs_create, }; @@ -1488,6 +1910,28 @@ brcmf_pcie_adjust_ramsize(struct brcmf_pciedev_info *devinfo, u8 *data, } +static void +brcmf_pcie_write_rand(struct brcmf_pciedev_info *devinfo, u32 nvram_csm) +{ + struct brcmf_rand_metadata rand_data; + u8 rand_buf[BRCMF_ENTROPY_HOST_LEN]; + u32 count = BRCMF_ENTROPY_HOST_LEN; + u32 address; + + address = devinfo->ci->rambase + + (devinfo->ci->ramsize - BRCMF_NVRAM_OFFSET_TCM) - + ((nvram_csm & 0xffff) * BRCMF_NVRAM_COMPRS_FACTOR) - + sizeof(rand_data); + memset(rand_buf, 0, BRCMF_ENTROPY_HOST_LEN); + rand_data.signature = cpu_to_le32(BRCMF_NVRAM_RNG_SIGNATURE); + rand_data.count = cpu_to_le32(count); + brcmf_pcie_copy_mem_todev(devinfo, address, &rand_data, + sizeof(rand_data)); + address -= count; + get_random_bytes(rand_buf, count); + brcmf_pcie_copy_mem_todev(devinfo, address, rand_buf, count); +} + static int brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo, u32 sharedram_addr) @@ -1495,6 +1939,7 @@ brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo, struct brcmf_bus *bus = dev_get_drvdata(&devinfo->pdev->dev); struct brcmf_pcie_shared_info *shared; u32 addr; + u32 host_cap; shared = &devinfo->shared; shared->tcm_base_address = sharedram_addr; @@ -1534,6 +1979,30 @@ brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo, addr = sharedram_addr + BRCMF_SHARED_RING_INFO_ADDR_OFFSET; shared->ring_info_addr = brcmf_pcie_read_tcm32(devinfo, addr); + if (shared->version >= BRCMF_PCIE_SHARED_VERSION_6) { + host_cap = shared->version; + + /* Disable OOB Device Wake based DeepSleep State Machine */ + host_cap |= BRCMF_HOSTCAP_DS_NO_OOB_DW; + + devinfo->hostready = + ((shared->flags & BRCMF_PCIE_SHARED_HOSTRDY_DB1) + == BRCMF_PCIE_SHARED_HOSTRDY_DB1); + if (devinfo->hostready) { + brcmf_dbg(PCIE, "HostReady supported by dongle.\n"); + host_cap |= BRCMF_HOSTCAP_H2D_ENABLE_HOSTRDY; + } + devinfo->use_mailbox = + ((shared->flags & BRCMF_PCIE_SHARED_USE_MAILBOX) + == BRCMF_PCIE_SHARED_USE_MAILBOX); + devinfo->use_d0_inform = false; + addr = sharedram_addr + BRCMF_SHARED_HOST_CAP_OFFSET; + + brcmf_pcie_write_tcm32(devinfo, addr, host_cap); + } else { + devinfo->use_d0_inform = true; + } + brcmf_dbg(PCIE, "max rx buf post %d, rx dataoffset %d\n", shared->max_rxbufpost, shared->rx_dataoffset); @@ -1548,12 +2017,16 @@ static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo, u32 nvram_len) { struct brcmf_bus *bus = dev_get_drvdata(&devinfo->pdev->dev); + struct trx_header_le *trx = (struct trx_header_le *)fw->data; + u32 fw_size; u32 sharedram_addr; u32 sharedram_addr_written; u32 loop_counter; int err; u32 address; u32 resetintr; + u32 nvram_lenw; + u32 nvram_csm; brcmf_dbg(PCIE, "Halt ARM.\n"); err = brcmf_pcie_enter_download_state(devinfo); @@ -1561,28 +2034,64 @@ static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo, return err; brcmf_dbg(PCIE, "Download FW %s\n", devinfo->fw_name); - brcmf_pcie_copy_mem_todev(devinfo, devinfo->ci->rambase, - (void *)fw->data, fw->size); + address = devinfo->ci->rambase; + fw_size = fw->size; + if (trx->magic == cpu_to_le32(TRX_MAGIC)) { + address -= sizeof(struct trx_header_le); + fw_size = le32_to_cpu(trx->len); + } + brcmf_pcie_copy_mem_todev(devinfo, address, (void *)fw->data, fw_size); resetintr = get_unaligned_le32(fw->data); release_firmware(fw); - /* reset last 4 bytes of RAM address. to be used for shared - * area. This identifies when FW is running - */ - brcmf_pcie_write_ram32(devinfo, devinfo->ci->ramsize - 4, 0); + if (devinfo->ci->blhs) { + brcmf_pcie_bus_console_read(devinfo, false); + err = devinfo->ci->blhs->post_fwdl(devinfo->ci); + if (err) { + brcmf_err(bus, "FW download failed, err=%d\n", err); + return err; + } + + err = devinfo->ci->blhs->chk_validation(devinfo->ci); + if (err) { + brcmf_err(bus, "FW valication failed, err=%d\n", err); + return err; + } + } else { + /* reset last 4 bytes of RAM address. to be used for shared + * area. This identifies when FW is running + */ + brcmf_pcie_write_ram32(devinfo, devinfo->ci->ramsize - 4, 0); + } if (nvram) { brcmf_dbg(PCIE, "Download NVRAM %s\n", devinfo->nvram_name); address = devinfo->ci->rambase + devinfo->ci->ramsize - nvram_len; + if (devinfo->ci->blhs) + address -= 4; brcmf_pcie_copy_mem_todev(devinfo, address, nvram, nvram_len); + + /* Convert nvram_len to words to determine the length token */ + nvram_lenw = nvram_len / 4; + nvram_csm = (~nvram_lenw << 16) | (nvram_lenw & 0x0000FFFF); brcmf_fw_nvram_free(nvram); } else { + nvram_csm = 0; brcmf_dbg(PCIE, "No matching NVRAM file found %s\n", devinfo->nvram_name); } + if (devinfo->ci->chip == CY_CC_55572_CHIP_ID) { + /* Write the length token to the last word of RAM address */ + brcmf_pcie_write_ram32(devinfo, devinfo->ci->ramsize - 4, + cpu_to_le32(nvram_csm)); + + /* Write random numbers to TCM for randomizing heap address */ + brcmf_pcie_write_rand(devinfo, nvram_csm); + } + sharedram_addr_written = brcmf_pcie_read_ram32(devinfo, devinfo->ci->ramsize - 4); @@ -1591,6 +2100,9 @@ static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo, if (err) return err; + if (!brcmf_pcie_bus_readshared(devinfo, nvram_csm)) + brcmf_pcie_bus_console_read(devinfo, false); + brcmf_dbg(PCIE, "Wait for FW init\n"); sharedram_addr = sharedram_addr_written; loop_counter = BRCMF_PCIE_FW_UP_TIMEOUT / 50; @@ -1647,6 +2159,9 @@ static int brcmf_pcie_get_resource(struct brcmf_pciedev_info *devinfo) devinfo->regs = ioremap(bar0_addr, BRCMF_PCIE_REG_MAP_SIZE); devinfo->tcm = ioremap(bar1_addr, bar1_size); +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + devinfo->bar1_size = bar1_size; +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ if (!devinfo->regs || !devinfo->tcm) { brcmf_err(bus, "ioremap() failed (%p,%p)\n", devinfo->regs, @@ -1673,6 +2188,21 @@ static void brcmf_pcie_release_resource(struct brcmf_pciedev_info *devinfo) pci_disable_device(devinfo->pdev); } +static u32 brcmf_pcie_buscore_blhs_read(void *ctx, u32 reg_offset) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx; + + brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); + return brcmf_pcie_read_reg32(devinfo, reg_offset); +} + +static void brcmf_pcie_buscore_blhs_write(void *ctx, u32 reg_offset, u32 value) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx; + + brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); + brcmf_pcie_write_reg32(devinfo, reg_offset, value); +} static u32 brcmf_pcie_buscore_prep_addr(const struct pci_dev *pdev, u32 addr) { @@ -1718,9 +2248,11 @@ static int brcmf_pcie_buscore_reset(void *ctx, struct brcmf_chip *chip) devinfo->ci = chip; brcmf_pcie_reset_device(devinfo); - val = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT); + val = brcmf_pcie_read_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_MAILBOXINT(devinfo)); if (val != 0xffffffff) - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, + brcmf_pcie_write_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_MAILBOXINT(devinfo), val); return 0; @@ -1736,12 +2268,63 @@ static void brcmf_pcie_buscore_activate(void *ctx, struct brcmf_chip *chip, } +static int brcmf_pcie_buscore_blhs_attach(void *ctx, struct brcmf_blhs **blhs, + u32 flag, uint timeout, uint interval) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx; + struct brcmf_bus *bus = dev_get_drvdata(&devinfo->pdev->dev); + struct brcmf_blhs *blhsh; + u32 regdata; + u32 pcie_enum; + u32 addr; + + if (devinfo->pdev->vendor != CY_PCIE_VENDOR_ID_CYPRESS) + return 0; + + pci_read_config_dword(devinfo->pdev, BRCMF_PCIE_CFGREG_REVID, ®data); + if (regdata & BRCMF_PCIE_CFGREG_REVID_SECURE_MODE) { + blhsh = kzalloc(sizeof(*blhsh), GFP_KERNEL); + if (!blhsh) + return -ENOMEM; + + blhsh->d2h = BRCMF_PCIE_PCIE2REG_DAR_D2H_MSG_0; + blhsh->h2d = BRCMF_PCIE_PCIE2REG_DAR_H2D_MSG_0; + blhsh->read = brcmf_pcie_buscore_blhs_read; + blhsh->write = brcmf_pcie_buscore_blhs_write; + + /* Host indication for bootloarder to start the init */ + if (devinfo->pdev->device == CY_PCIE_55572_DEVICE_ID) + pcie_enum = BRCMF_CYW55572_PCIE_BAR0_PCIE_ENUM_OFFSET; + else + pcie_enum = BRCMF_PCIE_BAR0_PCIE_ENUM_OFFSET; + + pci_read_config_dword(devinfo->pdev, PCI_BASE_ADDRESS_0, + ®data); + addr = regdata + pcie_enum + blhsh->h2d; + brcmf_pcie_buscore_write32(ctx, addr, 0); + + addr = regdata + pcie_enum + blhsh->d2h; + SPINWAIT_MS((brcmf_pcie_buscore_read32(ctx, addr) & flag) == 0, + timeout, interval); + regdata = brcmf_pcie_buscore_read32(ctx, addr); + if (!(regdata & flag)) { + brcmf_err(bus, "Timeout waiting for bootloader ready\n"); + kfree(blhsh); + return -EPERM; + } + *blhs = blhsh; + } + + return 0; +} + static const struct brcmf_buscore_ops brcmf_pcie_buscore_ops = { .prepare = brcmf_pcie_buscoreprep, .reset = brcmf_pcie_buscore_reset, .activate = brcmf_pcie_buscore_activate, .read32 = brcmf_pcie_buscore_read32, .write32 = brcmf_pcie_buscore_write32, + .blhs_attach = brcmf_pcie_buscore_blhs_attach, }; #define BRCMF_PCIE_FW_CODE 0 @@ -1758,13 +2341,14 @@ static void brcmf_pcie_setup(struct device *dev, int ret, struct brcmf_commonring **flowrings; u32 i, nvram_len; + bus = dev_get_drvdata(dev); + pcie_bus_dev = bus->bus_priv.pcie; + devinfo = pcie_bus_dev->devinfo; + /* check firmware loading result */ if (ret) goto fail; - bus = dev_get_drvdata(dev); - pcie_bus_dev = bus->bus_priv.pcie; - devinfo = pcie_bus_dev->devinfo; brcmf_pcie_attach(devinfo); fw = fwreq->items[BRCMF_PCIE_FW_CODE].binary; @@ -1786,8 +2370,11 @@ static void brcmf_pcie_setup(struct device *dev, int ret, brcmf_pcie_adjust_ramsize(devinfo, (u8 *)fw->data, fw->size); ret = brcmf_pcie_download_fw_nvram(devinfo, fw, nvram, nvram_len); - if (ret) + if (ret) { + if (devinfo->ci->blhs && !brcmf_pcie_bus_readshared(devinfo, 0)) + brcmf_pcie_bus_console_read(devinfo, true); goto fail; + } devinfo->state = BRCMFMAC_PCIE_STATE_UP; @@ -1827,15 +2414,20 @@ static void brcmf_pcie_setup(struct device *dev, int ret, brcmf_pcie_intr_enable(devinfo); brcmf_pcie_hostready(devinfo); - ret = brcmf_attach(&devinfo->pdev->dev); + ret = brcmf_attach(&devinfo->pdev->dev, true); if (ret) goto fail; brcmf_pcie_bus_console_read(devinfo, false); + brcmf_pcie_fwcon_timer(devinfo, true); + return; fail: + brcmf_err(bus, "Dongle setup failed\n"); + brcmf_pcie_bus_console_read(devinfo, true); + brcmf_fw_crashed(dev); device_release_driver(dev); } @@ -1847,15 +2439,23 @@ brcmf_pcie_prepare_fw_request(struct brcmf_pciedev_info *devinfo) { ".bin", devinfo->fw_name }, { ".txt", devinfo->nvram_name }, }; + u32 chip; + + if (devinfo->ci->blhs) + fwnames[BRCMF_PCIE_FW_CODE].extension = ".trxse"; - fwreq = brcmf_fw_alloc_request(devinfo->ci->chip, devinfo->ci->chiprev, + chip = devinfo->ci->chip; + fwreq = brcmf_fw_alloc_request(chip, devinfo->ci->chiprev, brcmf_pcie_fwnames, ARRAY_SIZE(brcmf_pcie_fwnames), fwnames, ARRAY_SIZE(fwnames)); if (!fwreq) return NULL; - fwreq->items[BRCMF_PCIE_FW_CODE].type = BRCMF_FW_TYPE_BINARY; + if (devinfo->ci->blhs) + fwreq->items[BRCMF_PCIE_FW_CODE].type = BRCMF_FW_TYPE_TRXSE; + else + fwreq->items[BRCMF_PCIE_FW_CODE].type = BRCMF_FW_TYPE_BINARY; fwreq->items[BRCMF_PCIE_FW_NVRAM].type = BRCMF_FW_TYPE_NVRAM; fwreq->items[BRCMF_PCIE_FW_NVRAM].flags = BRCMF_FW_REQF_OPTIONAL; fwreq->board_type = devinfo->settings->board_type; @@ -1866,6 +2466,105 @@ brcmf_pcie_prepare_fw_request(struct brcmf_pciedev_info *devinfo) return fwreq; } +#ifdef DEBUG +static void +brcmf_pcie_fwcon_timer(struct brcmf_pciedev_info *devinfo, bool active) +{ + if (!active) { + if (devinfo->console_active) { + del_timer_sync(&devinfo->timer); + devinfo->console_active = false; + } + return; + } + + /* don't start the timer */ + if (devinfo->state != BRCMFMAC_PCIE_STATE_UP || + !devinfo->console_interval || !BRCMF_FWCON_ON()) + return; + + if (!devinfo->console_active) { + devinfo->timer.expires = jiffies + devinfo->console_interval; + add_timer(&devinfo->timer); + devinfo->console_active = true; + } else { + /* Reschedule the timer */ + mod_timer(&devinfo->timer, jiffies + devinfo->console_interval); + } +} + +static void +brcmf_pcie_fwcon(struct timer_list *t) +{ + struct brcmf_pciedev_info *devinfo = from_timer(devinfo, t, timer); + + if (!devinfo->console_active) + return; + + brcmf_pcie_bus_console_read(devinfo, false); + + /* Reschedule the timer if console interval is not zero */ + mod_timer(&devinfo->timer, jiffies + devinfo->console_interval); +} + +static int brcmf_pcie_console_interval_get(void *data, u64 *val) +{ + struct brcmf_pciedev_info *devinfo = data; + + *val = devinfo->console_interval; + + return 0; +} + +static int brcmf_pcie_console_interval_set(void *data, u64 val) +{ + struct brcmf_pciedev_info *devinfo = data; + + if (val > MAX_CONSOLE_INTERVAL) + return -EINVAL; + + devinfo->console_interval = val; + + if (!val && devinfo->console_active) + brcmf_pcie_fwcon_timer(devinfo, false); + else if (val) + brcmf_pcie_fwcon_timer(devinfo, true); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(brcmf_pcie_console_interval_fops, + brcmf_pcie_console_interval_get, + brcmf_pcie_console_interval_set, + "%llu\n"); + +static void brcmf_pcie_debugfs_create(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + struct brcmf_pciedev *pcie_bus_dev = bus_if->bus_priv.pcie; + struct brcmf_pciedev_info *devinfo = pcie_bus_dev->devinfo; + struct dentry *dentry = brcmf_debugfs_get_devdir(drvr); + + if (IS_ERR_OR_NULL(dentry)) + return; + + devinfo->console_interval = BRCMF_CONSOLE; + + debugfs_create_file("console_interval", 0644, dentry, devinfo, + &brcmf_pcie_console_interval_fops); +} + +#else +void brcmf_pcie_fwcon_timer(struct brcmf_pciedev_info *devinfo, bool active) +{ +} + +static void brcmf_pcie_debugfs_create(struct device *dev) +{ +} +#endif + static int brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -1933,6 +2632,10 @@ brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (ret) goto fail_bus; +#ifdef DEBUG + /* Set up the fwcon timer */ + timer_setup(&devinfo->timer, brcmf_pcie_fwcon, 0); +#endif fwreq = brcmf_pcie_prepare_fw_request(devinfo); if (!fwreq) { ret = -ENOMEM; @@ -1978,6 +2681,8 @@ brcmf_pcie_remove(struct pci_dev *pdev) devinfo = bus->bus_priv.pcie->devinfo; + brcmf_pcie_fwcon_timer(devinfo, false); + devinfo->state = BRCMFMAC_PCIE_STATE_DOWN; if (devinfo->ci) brcmf_pcie_intr_disable(devinfo); @@ -2013,11 +2718,24 @@ static int brcmf_pcie_pm_enter_D3(struct device *dev) { struct brcmf_pciedev_info *devinfo; struct brcmf_bus *bus; + struct brcmf_cfg80211_info *config; + int retry = BRCMF_PM_WAIT_MAXRETRY; brcmf_dbg(PCIE, "Enter\n"); bus = dev_get_drvdata(dev); devinfo = bus->bus_priv.pcie->devinfo; + config = bus->drvr->config; + + while (retry && + config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING) { + usleep_range(10000, 20000); + retry--; + } + if (!retry && config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING) + brcmf_err(bus, "timed out wait for cfg80211 suspended\n"); + + brcmf_pcie_fwcon_timer(devinfo, false); brcmf_bus_change_state(bus, BRCMF_BUS_DOWN); @@ -2054,14 +2772,26 @@ static int brcmf_pcie_pm_leave_D3(struct device *dev) /* Check if device is still up and running, if so we are ready */ if (brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_INTMASK) != 0) { brcmf_dbg(PCIE, "Try to wakeup device....\n"); - if (brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D0_INFORM)) - goto cleanup; + if (devinfo->use_d0_inform) { + if (brcmf_pcie_send_mb_data(devinfo, + BRCMF_H2D_HOST_D0_INFORM)) + goto cleanup; + } else { + brcmf_pcie_hostready(devinfo); + } + brcmf_dbg(PCIE, "Hot resume, continue....\n"); devinfo->state = BRCMFMAC_PCIE_STATE_UP; brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); brcmf_bus_change_state(bus, BRCMF_BUS_UP); brcmf_pcie_intr_enable(devinfo); - brcmf_pcie_hostready(devinfo); + if (devinfo->use_d0_inform) { + brcmf_dbg(TRACE, "sending brcmf_pcie_hostready since use_d0_inform=%d\n", + devinfo->use_d0_inform); + brcmf_pcie_hostready(devinfo); + } + + brcmf_pcie_fwcon_timer(devinfo, true); return 0; } @@ -2096,6 +2826,9 @@ static const struct dev_pm_ops brcmf_pciedrvr_pm = { BRCM_PCIE_VENDOR_ID_BROADCOM, dev_id,\ subvend, subdev, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 } +#define BRCMF_PCIE_DEVICE_CY(dev_id) { CY_PCIE_VENDOR_ID_CYPRESS, dev_id,\ + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 } + static const struct pci_device_id brcmf_pcie_devid_table[] = { BRCMF_PCIE_DEVICE(BRCM_PCIE_4350_DEVICE_ID), BRCMF_PCIE_DEVICE_SUB(0x4355, BRCM_PCIE_VENDOR_ID_BROADCOM, 0x4355), @@ -2103,6 +2836,7 @@ static const struct pci_device_id brcmf_pcie_devid_table[] = { BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_RAW_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_4358_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_4359_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_DEVICE_ID), @@ -2118,13 +2852,21 @@ static const struct pci_device_id brcmf_pcie_devid_table[] = { BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_2G_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_5G_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_4371_DEVICE_ID), + BRCMF_PCIE_DEVICE(CY_PCIE_89459_DEVICE_ID), + BRCMF_PCIE_DEVICE(CY_PCIE_89459_RAW_DEVICE_ID), + BRCMF_PCIE_DEVICE(CY_PCIE_54591_DEVICE_ID), + BRCMF_PCIE_DEVICE(CY_PCIE_54590_DEVICE_ID), + BRCMF_PCIE_DEVICE(CY_PCIE_54594_DEVICE_ID), + BRCMF_PCIE_DEVICE_CY(CY_PCIE_55572_DEVICE_ID), + BRCMF_PCIE_DEVICE(CY_PCIE_4373_RAW_DEVICE_ID), + BRCMF_PCIE_DEVICE(CY_PCIE_4373_DUAL_DEVICE_ID), + BRCMF_PCIE_DEVICE(CY_PCIE_4373_2G_DEVICE_ID), + BRCMF_PCIE_DEVICE(CY_PCIE_4373_5G_DEVICE_ID), { /* end: all zeroes */ } }; - MODULE_DEVICE_TABLE(pci, brcmf_pcie_devid_table); - static struct pci_driver brcmf_pciedrvr = { .node = {}, .name = KBUILD_MODNAME, @@ -2137,7 +2879,6 @@ static struct pci_driver brcmf_pciedrvr = { .driver.coredump = brcmf_dev_coredump, }; - void brcmf_pcie_register(void) { int err; @@ -2149,7 +2890,6 @@ void brcmf_pcie_register(void) err); } - void brcmf_pcie_exit(void) { brcmf_dbg(PCIE, "Enter\n"); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h index d026401d20010..4a15975fe8c95 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h @@ -14,6 +14,6 @@ struct brcmf_pciedev { void brcmf_pcie_exit(void); void brcmf_pcie_register(void); - +void brcmf_pcie_handle_mb_data(struct brcmf_bus *bus_if, u32 d2h_mb_data); #endif /* BRCMFMAC_PCIE_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c index fabfbb0b40b0c..d0a7465be586d 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c @@ -158,12 +158,12 @@ static int brcmf_pno_set_random(struct brcmf_if *ifp, struct brcmf_pno_info *pi) struct brcmf_pno_macaddr_le pfn_mac; u8 *mac_addr = NULL; u8 *mac_mask = NULL; - int err, i; + int err, i, ri; - for (i = 0; i < pi->n_reqs; i++) - if (pi->reqs[i]->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { - mac_addr = pi->reqs[i]->mac_addr; - mac_mask = pi->reqs[i]->mac_addr_mask; + for (ri = 0; ri < pi->n_reqs; ri++) + if (pi->reqs[ri]->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { + mac_addr = pi->reqs[ri]->mac_addr; + mac_mask = pi->reqs[ri]->mac_addr_mask; break; } @@ -185,7 +185,7 @@ static int brcmf_pno_set_random(struct brcmf_if *ifp, struct brcmf_pno_info *pi) pfn_mac.mac[0] |= 0x02; brcmf_dbg(SCAN, "enabling random mac: reqid=%llu mac=%pM\n", - pi->reqs[i]->reqid, pfn_mac.mac); + pi->reqs[ri]->reqid, pfn_mac.mac); err = brcmf_fil_iovar_data_set(ifp, "pfn_macaddr", &pfn_mac, sizeof(pfn_mac)); if (err) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index 59c2b2b6027da..9da21d3b1af35 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -3,6 +3,7 @@ * Copyright (c) 2010 Broadcom Corporation */ +#include #include #include #include @@ -23,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -35,14 +37,20 @@ #include "core.h" #include "common.h" #include "bcdc.h" +#include "fwil.h" +#include "bt_shared_sdio.h" +#include "trxhdr.h" #define DCMD_RESP_TIMEOUT msecs_to_jiffies(2500) #define CTL_DONE_TIMEOUT msecs_to_jiffies(2500) +#define ULP_HUDI_PROC_DONE_TIME msecs_to_jiffies(2500) /* watermark expressed in number of words */ #define DEFAULT_F2_WATERMARK 0x8 -#define CY_4373_F2_WATERMARK 0x40 -#define CY_4373_F1_MESBUSYCTRL (CY_4373_F2_WATERMARK | SBSDIO_MESBUSYCTRL_ENAB) +#define CY_4373_F2_WATERMARK 0x4C +#define CY_4373_MES_WATERMARK 0x44 +#define CY_4373_MESBUSYCTRL (CY_4373_MES_WATERMARK | \ + SBSDIO_MESBUSYCTRL_ENAB) #define CY_43012_F2_WATERMARK 0x60 #define CY_43012_MES_WATERMARK 0x50 #define CY_43012_MESBUSYCTRL (CY_43012_MES_WATERMARK | \ @@ -58,6 +66,14 @@ #define CY_435X_F2_WATERMARK 0x40 #define CY_435X_F1_MESBUSYCTRL (CY_435X_F2_WATERMARK | \ SBSDIO_MESBUSYCTRL_ENAB) +#define CY_89459_F2_WATERMARK 0x40 +#define CY_89459_MES_WATERMARK 0x40 +#define CY_89459_MESBUSYCTRL (CY_89459_MES_WATERMARK | \ + SBSDIO_MESBUSYCTRL_ENAB) +#define CYW55572_F2_WATERMARK 0x40 +#define CYW55572_MES_WATERMARK 0x40 +#define CYW55572_F1_MESBUSYCTRL (CYW55572_MES_WATERMARK | \ + SBSDIO_MESBUSYCTRL_ENAB) #ifdef DEBUG @@ -135,8 +151,6 @@ struct rte_console { #define BRCMF_FIRSTREAD (1 << 6) -#define BRCMF_CONSOLE 10 /* watchdog interval to poll console */ - /* SBSDIO_DEVICE_CTL */ /* 1: device will assert busy signal when receiving CMD53 */ @@ -327,7 +341,16 @@ struct rte_console { #define KSO_WAIT_US 50 #define MAX_KSO_ATTEMPTS (PMU_MAX_TRANSITION_DLY/KSO_WAIT_US) -#define BRCMF_SDIO_MAX_ACCESS_ERRORS 5 +#define BRCMF_SDIO_MAX_ACCESS_ERRORS 20 + +static void brcmf_sdio_firmware_callback(struct device *dev, int err, + struct brcmf_fw_request *fwreq); +static struct brcmf_fw_request * + brcmf_sdio_prepare_fw_request(struct brcmf_sdio *bus); +static int brcmf_sdio_f2_ready(struct brcmf_sdio *bus); +static int brcmf_ulp_event_notify(struct brcmf_if *ifp, + const struct brcmf_event_msg *evtmsg, + void *data); #ifdef DEBUG /* Device console log buffer state */ @@ -610,20 +633,23 @@ BRCMF_FW_DEF(43241B5, "brcmfmac43241b5-sdio"); BRCMF_FW_DEF(4329, "brcmfmac4329-sdio"); BRCMF_FW_DEF(4330, "brcmfmac4330-sdio"); BRCMF_FW_DEF(4334, "brcmfmac4334-sdio"); -BRCMF_FW_DEF(43340, "brcmfmac43340-sdio"); +CY_FW_DEF(43340, "cyfmac43340-sdio"); BRCMF_FW_DEF(4335, "brcmfmac4335-sdio"); -BRCMF_FW_DEF(43362, "brcmfmac43362-sdio"); -BRCMF_FW_DEF(4339, "brcmfmac4339-sdio"); +CY_FW_DEF(43362, "cyfmac43362-sdio"); +CY_FW_DEF(4339, "cyfmac4339-sdio"); BRCMF_FW_DEF(43430A0, "brcmfmac43430a0-sdio"); /* Note the names are not postfixed with a1 for backward compatibility */ -BRCMF_FW_DEF(43430A1, "brcmfmac43430-sdio"); -BRCMF_FW_DEF(43455, "brcmfmac43455-sdio"); +CY_FW_DEF(43430A1, "cyfmac43430-sdio"); +CY_FW_DEF(43439, "cyfmac43439-sdio"); +CY_FW_DEF(43455, "cyfmac43455-sdio"); BRCMF_FW_DEF(43456, "brcmfmac43456-sdio"); -BRCMF_FW_DEF(4354, "brcmfmac4354-sdio"); -BRCMF_FW_DEF(4356, "brcmfmac4356-sdio"); -BRCMF_FW_DEF(4359, "brcmfmac4359-sdio"); -BRCMF_FW_DEF(4373, "brcmfmac4373-sdio"); -BRCMF_FW_DEF(43012, "brcmfmac43012-sdio"); +CY_FW_DEF(4354, "cyfmac4354-sdio"); +CY_FW_DEF(4356, "cyfmac4356-sdio"); +CY_FW_DEF(4359, "cyfmac4359-sdio"); +CY_FW_DEF(4373, "cyfmac4373-sdio"); +CY_FW_DEF(43012, "cyfmac43012-sdio"); +CY_FW_DEF(89459, "cyfmac54591-sdio"); +CY_FW_TRXSE_DEF(55572, "cyfmac55572-sdio"); static const struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = { BRCMF_FW_ENTRY(BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, 43143), @@ -639,14 +665,18 @@ static const struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = { BRCMF_FW_ENTRY(BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, 43362), BRCMF_FW_ENTRY(BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, 4339), BRCMF_FW_ENTRY(BRCM_CC_43430_CHIP_ID, 0x00000001, 43430A0), - BRCMF_FW_ENTRY(BRCM_CC_43430_CHIP_ID, 0xFFFFFFFE, 43430A1), + BRCMF_FW_ENTRY(BRCM_CC_43430_CHIP_ID, 0x0000001E, 43430A1), + BRCMF_FW_ENTRY(BRCM_CC_43430_CHIP_ID, 0xFFFFFFE0, 43439), BRCMF_FW_ENTRY(BRCM_CC_4345_CHIP_ID, 0x00000200, 43456), BRCMF_FW_ENTRY(BRCM_CC_4345_CHIP_ID, 0xFFFFFDC0, 43455), BRCMF_FW_ENTRY(BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, 4354), BRCMF_FW_ENTRY(BRCM_CC_4356_CHIP_ID, 0xFFFFFFFF, 4356), BRCMF_FW_ENTRY(BRCM_CC_4359_CHIP_ID, 0xFFFFFFFF, 4359), + BRCMF_FW_ENTRY(CY_CC_43439_CHIP_ID, 0xFFFFFFFF, 43439), BRCMF_FW_ENTRY(CY_CC_4373_CHIP_ID, 0xFFFFFFFF, 4373), - BRCMF_FW_ENTRY(CY_CC_43012_CHIP_ID, 0xFFFFFFFF, 43012) + BRCMF_FW_ENTRY(CY_CC_43012_CHIP_ID, 0xFFFFFFFF, 43012), + BRCMF_FW_ENTRY(CY_CC_89459_CHIP_ID, 0xFFFFFFFF, 89459), + BRCMF_FW_ENTRY(CY_CC_55572_CHIP_ID, 0xFFFFFFFF, 55572) }; #define TXCTL_CREDITS 2 @@ -707,7 +737,8 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on) * fail. Thereby just bailing out immediately after clearing KSO * bit, to avoid polling of KSO bit. */ - if (!on && bus->ci->chip == CY_CC_43012_CHIP_ID) + if (!on && ((bus->ci->chip == CY_CC_43012_CHIP_ID) || + (bus->ci->chip == CY_CC_55572_CHIP_ID))) return err; if (on) { @@ -924,6 +955,20 @@ static int brcmf_sdio_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) break; case CLK_SDONLY: +#ifdef CONFIG_BRCMFMAC_BT_SHARED_SDIO + /* If the request is to switch off backplane clock, + * confirm that BT is inactive before doing so. + * If this call had come from Non Watchdog context any way + * the Watchdog would switch off the clock again when + * nothing is to be done & BT has finished using the bus. + */ + if (brcmf_btsdio_bus_count(bus->sdiodev->bus_if)) { + brcmf_dbg(SDIO, "BT is active, not switching off\n"); + brcmf_sdio_wd_timer(bus, true); + break; + } + +#endif /* CONFIG_BRCMFMAC_BT_SHARED_SDIO */ /* Remove HT request, or bring up SD clock */ if (bus->clkstate == CLK_NONE) brcmf_sdio_sdclk(bus, true); @@ -935,6 +980,19 @@ static int brcmf_sdio_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) break; case CLK_NONE: +#ifdef CONFIG_BRCMFMAC_BT_SHARED_SDIO + /* If the request is to switch off backplane clock, + * confirm that BT is inactive before doing so. + * If this call had come from non-watchdog context any way + * the watchdog would switch off the clock again when + * nothing is to be done & BT has finished using the bus. + */ + if (brcmf_btsdio_bus_count(bus->sdiodev->bus_if)) { + brcmf_dbg(SDIO, "BT is active, not switching off\n"); + break; + } +#endif /* CONFIG_BRCMFMAC_BT_SHARED_SDIO */ + /* Make sure to remove HT request */ if (bus->clkstate == CLK_AVAIL) brcmf_sdio_htclk(bus, false, false); @@ -959,6 +1017,29 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) (sleep ? "SLEEP" : "WAKE"), (bus->sleeping ? "SLEEP" : "WAKE")); +#ifdef CONFIG_BRCMFMAC_BT_SHARED_SDIO + /* The following is the assumption based on which the hook is placed. + * From WLAN driver, either from the active contexts OR from the + * watchdog contexts, we will be attempting to go to sleep. At that + * moment if we see that BT is still actively using the bus, we will + * return -EBUSY from here, and the bus sleep state would not have + * changed, so the caller can then schedule the watchdog again + * which will come and attempt to sleep at a later point. + * + * In case if BT is the only one and is the last user, we don't switch + * off the clock immediately, we allow the WLAN to decide when to sleep + * i.e from the watchdog. + * Now if the watchdog becomes active and attempts to switch off the + * clock and if another WLAN context is active they are any way + * serialized with sdlock. + */ + if (brcmf_btsdio_bus_count(bus->sdiodev->bus_if)) { + brcmf_dbg(SDIO, "Cannot sleep when BT is active\n"); + err = -EBUSY; + goto done; + } +#endif /* CONFIG_BRCMFMAC_BT_SHARED_SDIO */ + /* If SR is enabled control bus state with KSO */ if (bus->sr_enabled) { /* Done if we're already in the requested state */ @@ -1093,7 +1174,7 @@ static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus) } #endif /* DEBUG */ -static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus) +static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus, u32 *hmbd) { struct brcmf_sdio_dev *sdiod = bus->sdiodev; struct brcmf_core *core = bus->sdio_core; @@ -1182,6 +1263,9 @@ static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus) HMB_DATA_FCDATA_MASK | HMB_DATA_VERSION_MASK)) brcmf_err("Unknown mailbox data content: 0x%02x\n", hmb_data); + /* Populate hmb_data if argument is passed for DS1 check later */ + if (hmbd) + *hmbd = hmb_data; return intstatus; } @@ -2345,6 +2429,9 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) &prec_out); if (pkt == NULL) break; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) + skb_orphan(pkt); +#endif __skb_queue_tail(&pktq, pkt); } spin_unlock_bh(&bus->txq_lock); @@ -2451,6 +2538,15 @@ static bool brcmf_chip_is_ulp(struct brcmf_chip *ci) return false; } +static bool brcmf_sdio_use_ht_avail(struct brcmf_chip *ci) +{ + if (ci->chip == CY_CC_4373_CHIP_ID || + ci->chip == CY_CC_55572_CHIP_ID) + return true; + else + return false; +} + static void brcmf_sdio_bus_stop(struct device *dev) { struct brcmf_bus *bus_if = dev_get_drvdata(dev); @@ -2487,7 +2583,8 @@ static void brcmf_sdio_bus_stop(struct device *dev) &err); if (!err) { bpreq = saveclk; - bpreq |= brcmf_chip_is_ulp(bus->ci) ? + bpreq |= (brcmf_sdio_use_ht_avail(bus->ci) || + brcmf_chip_is_ulp(bus->ci)) ? SBSDIO_HT_AVAIL_REQ : SBSDIO_FORCE_HT; brcmf_sdiod_writeb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, @@ -2568,6 +2665,182 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) return ret; } +/* This Function is used to retrieve important + * details from dongle related to ULP mode Mostly + * values/SHM details that will be vary depending + * on the firmware branches + */ +static void +brcmf_sdio_ulp_preinit(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + struct brcmf_if *ifp = bus_if->drvr->iflist[0]; + + brcmf_dbg(ULP, "Enter\n"); + + /* Query ulp_sdioctrl iovar to get the ULP related SHM offsets */ + brcmf_fil_iovar_data_get(ifp, "ulp_sdioctrl", + &sdiodev->fmac_ulp.ulp_shm_offset, + sizeof(sdiodev->fmac_ulp.ulp_shm_offset)); + + sdiodev->ulp = false; + + brcmf_dbg(ULP, "m_ulp_ctrl_sdio[%x] m_ulp_wakeevt_ind [%x]\n", + M_DS1_CTRL_SDIO(sdiodev->fmac_ulp), + M_WAKEEVENT_IND(sdiodev->fmac_ulp)); + brcmf_dbg(ULP, "m_ulp_wakeind [%x]\n", + M_ULP_WAKE_IND(sdiodev->fmac_ulp)); +} + +/* Reinitialize ARM because In DS1 mode ARM got off */ +static int +brcmf_sdio_ulp_reinit_fw(struct brcmf_sdio *bus) +{ + struct brcmf_sdio_dev *sdiodev = bus->sdiodev; + struct brcmf_fw_request *fwreq; + int err = 0; + + /* After firmware redownload tx/rx seq are reset accordingly + * these values are reset on FMAC side tx_max is initially set to 4, + * which later is updated by FW. + */ + bus->tx_seq = 0; + bus->rx_seq = 0; + bus->tx_max = 4; + + fwreq = brcmf_sdio_prepare_fw_request(bus); + if (!fwreq) + return -ENOMEM; + + err = brcmf_fw_get_firmwares(sdiodev->dev, fwreq, + brcmf_sdio_firmware_callback); + if (err != 0) { + brcmf_err("async firmware request failed: %d\n", err); + kfree(fwreq); + } + + return err; +} + +/* Check if device is in DS1 mode and handshake with ULP UCODE */ +static bool +brcmf_sdio_ulp_pre_redownload_check(struct brcmf_sdio *bus, u32 hmb_data) +{ + struct brcmf_sdio_dev *sdiod = bus->sdiodev; + int err = 0; + u32 value = 0; + u32 val32, ulp_wake_ind, wowl_wake_ind; + int reg_addr; + unsigned long timeout; + struct brcmf_ulp *fmac_ulp = &bus->sdiodev->fmac_ulp; + int i = 0; + + /* If any host mail box data is present, ignore DS1 exit sequence */ + if (hmb_data) + return false; + /* Skip if DS1 Exit is already in progress + * This can happen if firmware download is taking more time + */ + if (fmac_ulp->ulp_state == FMAC_ULP_TRIGGERED) + return false; + + value = brcmf_sdiod_func0_rb(sdiod, SDIO_CCCR_IOEx, &err); + + if (value == SDIO_FUNC_ENABLE_1) { + brcmf_dbg(ULP, "GOT THE INTERRUPT FROM UCODE\n"); + sdiod->ulp = true; + fmac_ulp->ulp_state = FMAC_ULP_TRIGGERED; + ulp_wake_ind = D11SHM_RDW(sdiod, + M_ULP_WAKE_IND(sdiod->fmac_ulp), + &err); + wowl_wake_ind = D11SHM_RDW(sdiod, + M_WAKEEVENT_IND(sdiod->fmac_ulp), + &err); + + brcmf_dbg(ULP, "wowl_wake_ind: 0x%08x, ulp_wake_ind: 0x%08x state %s\n", + wowl_wake_ind, ulp_wake_ind, (fmac_ulp->ulp_state) ? + "DS1 Exit Triggered" : "IDLE State"); + + if (wowl_wake_ind || ulp_wake_ind) { + /* RX wake Don't do anything. + * Just bail out and re-download firmware. + */ + /* Print out PHY TX error block when bit 9 set */ + if ((ulp_wake_ind & C_DS1_PHY_TXERR) && + M_DS1_PHYTX_ERR_BLK(sdiod->fmac_ulp)) { + brcmf_err("Dump PHY TX Error SHM Locations\n"); + for (i = 0; i < PHYTX_ERR_BLK_SIZE; i++) { + pr_err("0x%x", + D11SHM_RDW(sdiod, + (M_DS1_PHYTX_ERR_BLK(sdiod->fmac_ulp) + + (i * 2)), &err)); + } + brcmf_err("\n"); + } + } else { + /* TX wake negotiate with MAC */ + brcmf_dbg(ULP, "M_DS1_CTRL_SDIO: 0x%08x\n", + (u32)D11SHM_RDW(sdiod, + M_DS1_CTRL_SDIO(sdiod->fmac_ulp), + &err)); + val32 = D11SHM_RD(sdiod, + M_DS1_CTRL_SDIO(sdiod->fmac_ulp), + &err); + D11SHM_WR(sdiod, M_DS1_CTRL_SDIO(sdiod->fmac_ulp), + val32, (C_DS1_CTRL_SDIO_DS1_EXIT | + C_DS1_CTRL_REQ_VALID), &err); + val32 = D11REG_RD(sdiod, D11_MACCONTROL_REG, &err); + val32 = val32 | D11_MACCONTROL_REG_WAKE; + D11REG_WR(sdiod, D11_MACCONTROL_REG, val32, &err); + + /* Poll for PROC_DONE to be set by ucode */ + value = D11SHM_RDW(sdiod, + M_DS1_CTRL_SDIO(sdiod->fmac_ulp), + &err); + /* Wait here (polling) for C_DS1_CTRL_PROC_DONE */ + timeout = jiffies + ULP_HUDI_PROC_DONE_TIME; + while (!(value & C_DS1_CTRL_PROC_DONE)) { + value = D11SHM_RDW(sdiod, + M_DS1_CTRL_SDIO(sdiod->fmac_ulp), + &err); + if (time_after(jiffies, timeout)) + break; + usleep_range(1000, 2000); + } + brcmf_dbg(ULP, "M_DS1_CTRL_SDIO: 0x%08x\n", + (u32)D11SHM_RDW(sdiod, + M_DS1_CTRL_SDIO(sdiod->fmac_ulp), &err)); + value = D11SHM_RDW(sdiod, + M_DS1_CTRL_SDIO(sdiod->fmac_ulp), + &err); + if (!(value & C_DS1_CTRL_PROC_DONE)) { + brcmf_err("Timeout Failed to enter DS1 Exit state!\n"); + return false; + } + } + + ulp_wake_ind = D11SHM_RDW(sdiod, + M_ULP_WAKE_IND(sdiod->fmac_ulp), + &err); + wowl_wake_ind = D11SHM_RDW(sdiod, + M_WAKEEVENT_IND(sdiod->fmac_ulp), + &err); + brcmf_dbg(ULP, "wowl_wake_ind: 0x%08x, ulp_wake_ind: 0x%08x\n", + wowl_wake_ind, ulp_wake_ind); + reg_addr = CORE_CC_REG( + brcmf_chip_get_pmu(bus->ci)->base, min_res_mask); + brcmf_sdiod_writel(sdiod, reg_addr, + DEFAULT_43012_MIN_RES_MASK, &err); + if (err) + brcmf_err("min_res_mask failed\n"); + + return true; + } + + return false; +} + static void brcmf_sdio_dpc(struct brcmf_sdio *bus) { struct brcmf_sdio_dev *sdiod = bus->sdiodev; @@ -2635,12 +2908,18 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) atomic_set(&bus->fcstate, !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE))); intstatus |= (newstatus & bus->hostintmask); +#ifdef CONFIG_BRCMFMAC_BT_SHARED_SDIO + brcmf_btsdio_int_handler(bus->sdiodev->bus_if); +#endif /* CONFIG_BRCMFMAC_BT_SHARED_SDIO */ } /* Handle host mailbox indication */ if (intstatus & I_HMB_HOST_INT) { + u32 hmb_data = 0; intstatus &= ~I_HMB_HOST_INT; - intstatus |= brcmf_sdio_hostmail(bus); + intstatus |= brcmf_sdio_hostmail(bus, &hmb_data); + if (brcmf_sdio_ulp_pre_redownload_check(bus, hmb_data)) + brcmf_sdio_ulp_reinit_fw(bus); } sdio_release_host(bus->sdiodev->func1); @@ -2685,7 +2964,7 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) brcmf_sdio_clrintr(bus); if (bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL) && - txctl_ok(bus)) { + txctl_ok(bus) && brcmf_sdio_f2_ready(bus)) { sdio_claim_host(bus->sdiodev->func1); if (bus->ctrl_frame_stat) { err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf, @@ -2811,6 +3090,8 @@ static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) brcmf_dbg(TRACE, "deferring pktq len %d\n", pktq_len(&bus->txq)); bus->sdcnt.fcqueued++; + skb_tx_timestamp(pkt); + /* Priority based enq */ spin_lock_bh(&bus->txq_lock); /* reset bus_flags in packet cb */ @@ -3335,17 +3616,26 @@ brcmf_sdio_verifymemory(struct brcmf_sdio_dev *sdiodev, u32 ram_addr, static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus, const struct firmware *fw) { + struct trx_header_le *trx = (struct trx_header_le *)fw->data; + u32 fw_size; + u32 address; int err; brcmf_dbg(TRACE, "Enter\n"); - err = brcmf_sdiod_ramrw(bus->sdiodev, true, bus->ci->rambase, - (u8 *)fw->data, fw->size); + address = bus->ci->rambase; + fw_size = fw->size; + if (trx->magic == cpu_to_le32(TRX_MAGIC)) { + address -= sizeof(struct trx_header_le); + fw_size = le32_to_cpu(trx->len); + } + err = brcmf_sdiod_ramrw(bus->sdiodev, true, address, + (u8 *)fw->data, fw_size); if (err) brcmf_err("error %d on writing %d membytes at 0x%08x\n", - err, (int)fw->size, bus->ci->rambase); - else if (!brcmf_sdio_verifymemory(bus->sdiodev, bus->ci->rambase, - (u8 *)fw->data, fw->size)) + err, (int)fw_size, address); + else if (!brcmf_sdio_verifymemory(bus->sdiodev, address, + (u8 *)fw->data, fw_size)) err = -EIO; return err; @@ -3383,6 +3673,16 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus, rstvec = get_unaligned_le32(fw->data); brcmf_dbg(SDIO, "firmware rstvec: %x\n", rstvec); + if (bus->ci->blhs) { + bcmerror = bus->ci->blhs->prep_fwdl(bus->ci); + if (bcmerror) { + brcmf_err("FW download preparation failed\n"); + release_firmware(fw); + brcmf_fw_nvram_free(nvram); + goto err; + } + } + bcmerror = brcmf_sdio_download_code_file(bus, fw); release_firmware(fw); if (bcmerror) { @@ -3391,6 +3691,22 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus, goto err; } + if (bus->ci->blhs) { + bcmerror = bus->ci->blhs->post_fwdl(bus->ci); + if (bcmerror) { + brcmf_err("FW download failed, err=%d\n", bcmerror); + brcmf_fw_nvram_free(nvram); + goto err; + } + + bcmerror = bus->ci->blhs->chk_validation(bus->ci); + if (bcmerror) { + brcmf_err("FW valication failed, err=%d\n", bcmerror); + brcmf_fw_nvram_free(nvram); + goto err; + } + } + bcmerror = brcmf_sdio_download_nvram(bus, nvram, nvlen); brcmf_fw_nvram_free(nvram); if (bcmerror) { @@ -3398,10 +3714,14 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus, goto err; } - /* Take arm out of reset */ - if (!brcmf_chip_set_active(bus->ci, rstvec)) { - brcmf_err("error getting out of ARM core reset\n"); - goto err; + if (bus->ci->blhs) { + bus->ci->blhs->post_nvramdl(bus->ci); + } else { + /* Take arm out of reset */ + if (!brcmf_chip_set_active(bus->ci, rstvec)) { + brcmf_err("error getting out of ARM core reset\n"); + goto err; + } } err: @@ -3412,7 +3732,13 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus, static bool brcmf_sdio_aos_no_decode(struct brcmf_sdio *bus) { - if (bus->ci->chip == CY_CC_43012_CHIP_ID) + if (bus->ci->chip == CY_CC_43012_CHIP_ID || + bus->ci->chip == CY_CC_4373_CHIP_ID || + bus->ci->chip == CY_CC_55572_CHIP_ID || + bus->ci->chip == BRCM_CC_4339_CHIP_ID || + bus->ci->chip == BRCM_CC_4345_CHIP_ID || + bus->ci->chip == BRCM_CC_4354_CHIP_ID || + bus->ci->chip == BRCM_CC_4356_CHIP_ID) return true; else return false; @@ -3428,7 +3754,8 @@ static void brcmf_sdio_sr_init(struct brcmf_sdio *bus) brcmf_dbg(TRACE, "Enter\n"); - if (brcmf_chip_is_ulp(bus->ci)) { + if (brcmf_sdio_use_ht_avail(bus->ci) || + brcmf_chip_is_ulp(bus->ci)) { wakeupctrl = SBSDIO_FUNC1_WCTRL_ALPWAIT_SHIFT; chipclkcsr = SBSDIO_HT_AVAIL_REQ; } else { @@ -3552,6 +3879,10 @@ static int brcmf_sdio_bus_preinit(struct device *dev) if (err < 0) goto done; + /* initialize SHM address from firmware for DS1 */ + if (!bus->sdiodev->ulp) + brcmf_sdio_ulp_preinit(dev); + bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN; if (sdiodev->sg_support) { bus->txglom = false; @@ -3711,7 +4042,8 @@ static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) #endif /* DEBUG */ /* On idle timeout clear activity flag and/or turn off clock */ - if (!bus->dpc_triggered) { + if (!bus->dpc_triggered && + brcmf_btsdio_bus_count(bus->sdiodev->bus_if) == 0) { rmb(); if ((!bus->dpc_running) && (bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) { @@ -3826,6 +4158,20 @@ brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, } } +static u32 brcmf_sdio_buscore_blhs_read(void *ctx, u32 reg_offset) +{ + struct brcmf_sdio_dev *sdiodev = (struct brcmf_sdio_dev *)ctx; + + return (u32)brcmf_sdiod_readb(sdiodev, reg_offset, NULL); +} + +static void brcmf_sdio_buscore_blhs_write(void *ctx, u32 reg_offset, u32 value) +{ + struct brcmf_sdio_dev *sdiodev = (struct brcmf_sdio_dev *)ctx; + + brcmf_sdiod_writeb(sdiodev, reg_offset, (u8)value, NULL); +} + static int brcmf_sdio_buscoreprep(void *ctx) { struct brcmf_sdio_dev *sdiodev = ctx; @@ -3922,11 +4268,39 @@ static void brcmf_sdio_buscore_write32(void *ctx, u32 addr, u32 val) brcmf_sdiod_writel(sdiodev, addr, val, NULL); } +static int brcmf_sdio_buscore_blhs_attach(void *ctx, struct brcmf_blhs **blhs, + u32 flag, uint timeout, uint interval) +{ + struct brcmf_sdio_dev *sdiodev = (struct brcmf_sdio_dev *)ctx; + struct brcmf_blhs *blhsh; + u8 cardcap; + + if (sdiodev->func1->vendor != SDIO_VENDOR_ID_CYPRESS) + return 0; + + cardcap = brcmf_sdiod_func0_rb(sdiodev, SDIO_CCCR_BRCM_CARDCAP, NULL); + if (cardcap & SDIO_CCCR_BRCM_CARDCAP_SECURE_MODE) { + blhsh = kzalloc(sizeof(*blhsh), GFP_KERNEL); + if (!blhsh) + return -ENOMEM; + + blhsh->d2h = BRCMF_SDIO_REG_DAR_D2H_MSG_0; + blhsh->h2d = BRCMF_SDIO_REG_DAR_H2D_MSG_0; + blhsh->read = brcmf_sdio_buscore_blhs_read; + blhsh->write = brcmf_sdio_buscore_blhs_write; + + *blhs = blhsh; + } + + return 0; +} + static const struct brcmf_buscore_ops brcmf_sdio_buscore_ops = { .prepare = brcmf_sdio_buscoreprep, .activate = brcmf_sdio_buscore_activate, .read32 = brcmf_sdio_buscore_read32, .write32 = brcmf_sdio_buscore_write32, + .blhs_attach = brcmf_sdio_buscore_blhs_attach, }; static bool @@ -4035,17 +4409,21 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus) if (err) goto fail; - /* set PMUControl so a backplane reset does PMU state reload */ - reg_addr = CORE_CC_REG(brcmf_chip_get_pmu(bus->ci)->base, pmucontrol); - reg_val = brcmf_sdiod_readl(sdiodev, reg_addr, &err); - if (err) - goto fail; + if (!bus->ci->blhs) { + /* set PMUControl so a backplane reset does PMU state reload */ + reg_addr = CORE_CC_REG(brcmf_chip_get_pmu(bus->ci)->base, + pmucontrol); + reg_val = brcmf_sdiod_readl(sdiodev, reg_addr, &err); + if (err) + goto fail; - reg_val |= (BCMA_CC_PMU_CTL_RES_RELOAD << BCMA_CC_PMU_CTL_RES_SHIFT); + reg_val |= (BCMA_CC_PMU_CTL_RES_RELOAD << + BCMA_CC_PMU_CTL_RES_SHIFT); - brcmf_sdiod_writel(sdiodev, reg_addr, reg_val, &err); - if (err) - goto fail; + brcmf_sdiod_writel(sdiodev, reg_addr, reg_val, &err); + if (err) + goto fail; + } sdio_release_host(sdiodev->func1); @@ -4194,7 +4572,7 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err, u8 saveclk, bpreq; u8 devctl; - brcmf_dbg(TRACE, "Enter: dev=%s, err=%d\n", dev_name(dev), err); + brcmf_dbg(ULP, "Enter: dev=%s, err=%d\n", dev_name(dev), err); if (err) goto fail; @@ -4211,10 +4589,6 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err, goto fail; bus->alp_only = false; - /* Start the watchdog timer */ - bus->sdcnt.tickcnt = 0; - brcmf_sdio_wd_timer(bus, true); - sdio_claim_host(sdiod->func1); /* Make sure backplane clock is on, needed to generate F2 interrupt */ @@ -4226,7 +4600,8 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err, saveclk = brcmf_sdiod_readb(sdiod, SBSDIO_FUNC1_CHIPCLKCSR, &err); if (!err) { bpreq = saveclk; - bpreq |= brcmf_chip_is_ulp(bus->ci) ? + bpreq |= (brcmf_sdio_use_ht_avail(bus->ci) || + brcmf_chip_is_ulp(bus->ci)) ? SBSDIO_HT_AVAIL_REQ : SBSDIO_FORCE_HT; brcmf_sdiod_writeb(sdiod, SBSDIO_FUNC1_CHIPCLKCSR, bpreq, &err); @@ -4263,7 +4638,7 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err, brcmf_sdiod_writeb(sdiod, SBSDIO_DEVICE_CTL, devctl, &err); brcmf_sdiod_writeb(sdiod, SBSDIO_FUNC1_MESBUSYCTRL, - CY_4373_F1_MESBUSYCTRL, &err); + CY_4373_MESBUSYCTRL, &err); break; case SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012: brcmf_dbg(INFO, "set F2 watermark to 0x%x*4 bytes\n", @@ -4320,6 +4695,35 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err, brcmf_sdiod_writeb(sdiod, SBSDIO_FUNC1_MESBUSYCTRL, CY_435X_F1_MESBUSYCTRL, &err); break; + case SDIO_DEVICE_ID_BROADCOM_CYPRESS_89459: + case SDIO_DEVICE_ID_CYPRESS_54590: + case SDIO_DEVICE_ID_CYPRESS_54591: + case SDIO_DEVICE_ID_CYPRESS_54594: + brcmf_dbg(INFO, "set F2/MES watermark to 0x%x*4 / 0x%x bytes for 89459\n", + CY_89459_F2_WATERMARK, CY_89459_MESBUSYCTRL); + brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK, + CY_89459_F2_WATERMARK, &err); + devctl = brcmf_sdiod_readb(sdiod, SBSDIO_DEVICE_CTL, + &err); + devctl |= SBSDIO_DEVCTL_F2WM_ENAB; + brcmf_sdiod_writeb(sdiod, SBSDIO_DEVICE_CTL, devctl, + &err); + brcmf_sdiod_writeb(sdiod, SBSDIO_FUNC1_MESBUSYCTRL, + CY_89459_MESBUSYCTRL, &err); + break; + case SDIO_DEVICE_ID_CYPRESS_55572: + brcmf_dbg(INFO, "set F2 watermark to 0x%x*4 bytes\n", + CYW55572_F2_WATERMARK); + brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK, + CYW55572_F2_WATERMARK, &err); + devctl = brcmf_sdiod_readb(sdiod, SBSDIO_DEVICE_CTL, + &err); + devctl |= SBSDIO_DEVCTL_F2WM_ENAB; + brcmf_sdiod_writeb(sdiod, SBSDIO_DEVICE_CTL, devctl, + &err); + brcmf_sdiod_writeb(sdiod, SBSDIO_FUNC1_MESBUSYCTRL, + CYW55572_F1_MESBUSYCTRL, &err); + break; default: brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK, DEFAULT_F2_WATERMARK, &err); @@ -4360,6 +4764,9 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err, goto checkdied; } + /* Start the watchdog timer */ + bus->sdcnt.tickcnt = 0; + brcmf_sdio_wd_timer(bus, true); sdio_release_host(sdiod->func1); err = brcmf_alloc(sdiod->dev, sdiod->settings); @@ -4368,13 +4775,34 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err, goto claim; } +#ifdef CONFIG_BRCMFMAC_BT_SHARED_SDIO + err = brcmf_btsdio_init(bus_if); + if (err) { + brcmf_err("brcmf_btsdio_init failed\n"); + goto free; + } +#endif /* CONFIG_BRCMFMAC_BT_SHARED_SDIO */ + /* Attach to the common layer, reserve hdr space */ - err = brcmf_attach(sdiod->dev); + err = brcmf_attach(sdiod->dev, !bus->sdiodev->ulp); if (err != 0) { brcmf_err("brcmf_attach failed\n"); goto free; } + /* Register for ULP events */ + if (sdiod->func1->device == SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012) + brcmf_fweh_register(bus_if->drvr, BRCMF_E_ULP, + brcmf_ulp_event_notify); + + if (bus->sdiodev->ulp) { + /* For ULP, after firmware redownload complete + * set ULP state to IDLE + */ + if (bus->sdiodev->fmac_ulp.ulp_state == FMAC_ULP_TRIGGERED) + bus->sdiodev->fmac_ulp.ulp_state = FMAC_ULP_IDLE; + } + /* ready */ return; @@ -4401,6 +4829,9 @@ brcmf_sdio_prepare_fw_request(struct brcmf_sdio *bus) { ".txt", bus->sdiodev->nvram_name }, }; + if (bus->ci->blhs) + fwnames[BRCMF_SDIO_FW_CODE].extension = ".trxse"; + fwreq = brcmf_fw_alloc_request(bus->ci->chip, bus->ci->chiprev, brcmf_sdio_fwnames, ARRAY_SIZE(brcmf_sdio_fwnames), @@ -4408,7 +4839,10 @@ brcmf_sdio_prepare_fw_request(struct brcmf_sdio *bus) if (!fwreq) return NULL; - fwreq->items[BRCMF_SDIO_FW_CODE].type = BRCMF_FW_TYPE_BINARY; + if (bus->ci->blhs) + fwreq->items[BRCMF_SDIO_FW_CODE].type = BRCMF_FW_TYPE_TRXSE; + else + fwreq->items[BRCMF_SDIO_FW_CODE].type = BRCMF_FW_TYPE_BINARY; fwreq->items[BRCMF_SDIO_FW_NVRAM].type = BRCMF_FW_TYPE_NVRAM; fwreq->board_type = bus->sdiodev->settings->board_type; @@ -4438,7 +4872,7 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) bus->tx_seq = SDPCM_SEQ_WRAP - 1; /* single-threaded workqueue */ - wq = alloc_ordered_workqueue("brcmf_wq/%s", WQ_MEM_RECLAIM, + wq = alloc_ordered_workqueue("brcmf_wq/%s", WQ_MEM_RECLAIM | WQ_HIGHPRI, dev_name(&sdiodev->func1->dev)); if (!wq) { brcmf_err("insufficient memory to create txworkqueue\n"); @@ -4557,7 +4991,23 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) * necessary cores. */ msleep(20); - brcmf_chip_set_passive(bus->ci); + if (bus->sdiodev->fmac_ulp.ulp_state == + FMAC_ULP_ENTRY_RECV) { + brcmf_chip_ulp_reset_lhl_regs(bus->ci); + brcmf_chip_reset_pmu_regs(bus->ci); + } else { + brcmf_chip_set_passive(bus->ci); + } + + if (bus->ci->blhs) + bus->ci->blhs->init(bus->ci); + /* Reset the PMU, backplane and all the + * cores by using the PMUWatchdogCounter. + */ + brcmf_chip_reset_watchdog(bus->ci); + if (bus->ci->blhs) + bus->ci->blhs->post_wdreset(bus->ci); + brcmf_sdio_clkctl(bus, CLK_NONE, false); sdio_release_host(bus->sdiodev->func1); } @@ -4565,6 +5015,9 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) } if (bus->sdiodev->settings) brcmf_release_module_param(bus->sdiodev->settings); +#ifdef CONFIG_BRCMFMAC_BT_SHARED_SDIO + brcmf_btsdio_detach(bus->sdiodev->bus_if); +#endif /* CONFIG_BRCMFMAC_BT_SHARED_SDIO */ kfree(bus->rxbuf); kfree(bus->hdrbuf); @@ -4613,3 +5066,39 @@ int brcmf_sdio_sleep(struct brcmf_sdio *bus, bool sleep) return ret; } +/* Check F2 Ready bit before sending data to Firmware */ +static int +brcmf_sdio_f2_ready(struct brcmf_sdio *bus) +{ + int ret = -1; + int iordy_status = 0; + + sdio_claim_host(bus->sdiodev->func1); + /* Read the status of IOR2 */ + iordy_status = brcmf_sdiod_func0_rb(bus->sdiodev, SDIO_CCCR_IORx, NULL); + + sdio_release_host(bus->sdiodev->func1); + ret = iordy_status & SDIO_FUNC_ENABLE_2; + return ret; +} + +static int brcmf_ulp_event_notify(struct brcmf_if *ifp, + const struct brcmf_event_msg *evtmsg, + void *data) +{ + int err = 0; + struct brcmf_bus *bus_if = ifp->drvr->bus_if; + struct brcmf_sdio_dev *sdiodev; + struct brcmf_sdio *bus; + struct brcmf_ulp_event *ulp_event = (struct brcmf_ulp_event *)data; + + sdiodev = bus_if->bus_priv.sdio; + bus = sdiodev->bus; + + brcmf_dbg(ULP, "Chip went to DS1 state : action %d\n", + ulp_event->ulp_dongle_action); + if (ulp_event->ulp_dongle_action == FMAC_ULP_ENTRY) + bus->sdiodev->fmac_ulp.ulp_state = FMAC_ULP_ENTRY_RECV; + + return err; +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h index 15d2c02fa3ec8..d4166805a1978 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h @@ -28,12 +28,16 @@ #define REG_F0_REG_MASK 0x7FF #define REG_F1_MISC_MASK 0x1FFFF +#define BRCMF_SDIO_REG_DAR_H2D_MSG_0 0x10030 +#define BRCMF_SDIO_REG_DAR_D2H_MSG_0 0x10038 + /* function 0 vendor specific CCCR registers */ #define SDIO_CCCR_BRCM_CARDCAP 0xf0 #define SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT BIT(1) #define SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT BIT(2) #define SDIO_CCCR_BRCM_CARDCAP_CMD_NODEC BIT(3) +#define SDIO_CCCR_BRCM_CARDCAP_SECURE_MODE BIT(7) /* Interrupt enable bits for each function */ #define SDIO_CCCR_IEN_FUNC0 BIT(0) @@ -165,6 +169,35 @@ struct brcmf_sdreg { struct brcmf_sdio; struct brcmf_sdiod_freezer; +/* ULP SHM Offsets info */ +struct ulp_shm_info { + u32 m_ulp_ctrl_sdio; + u32 m_ulp_wakeevt_ind; + u32 m_ulp_wakeind; + u32 m_ulp_phytxblk; +}; + +/* FMAC ULP state machine */ +#define FMAC_ULP_IDLE (0) +#define FMAC_ULP_ENTRY_RECV (1) +#define FMAC_ULP_TRIGGERED (2) + +/* BRCMF_E_ULP event data */ +#define FMAC_ULP_EVENT_VERSION 1 +#define FMAC_ULP_DISABLE_CONSOLE 1 /* Disable console */ +#define FMAC_ULP_UCODE_DOWNLOAD 2 /* Download ULP ucode file */ +#define FMAC_ULP_ENTRY 3 /* Inform ulp entry to Host */ + +struct brcmf_ulp { + uint ulp_state; + struct ulp_shm_info ulp_shm_offset; +}; + +struct brcmf_ulp_event { + u16 version; + u16 ulp_dongle_action; +}; + struct brcmf_sdio_dev { struct sdio_func *func1; struct sdio_func *func2; @@ -189,6 +222,8 @@ struct brcmf_sdio_dev { bool wowl_enabled; enum brcmf_sdiod_state state; struct brcmf_sdiod_freezer *freezer; + struct brcmf_ulp fmac_ulp; + bool ulp; }; /* sdio core registers */ @@ -379,4 +414,83 @@ void brcmf_sdio_wowl_config(struct device *dev, bool enabled); int brcmf_sdio_sleep(struct brcmf_sdio *bus, bool sleep); void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus); +/* SHM offsets */ +#define M_DS1_CTRL_SDIO(ptr) ((ptr).ulp_shm_offset.m_ulp_ctrl_sdio) +#define M_WAKEEVENT_IND(ptr) ((ptr).ulp_shm_offset.m_ulp_wakeevt_ind) +#define M_ULP_WAKE_IND(ptr) ((ptr).ulp_shm_offset.m_ulp_wakeind) +#define M_DS1_PHYTX_ERR_BLK(ptr) ((ptr).ulp_shm_offset.m_ulp_phytxblk) + +#define D11_BASE_ADDR 0x18001000 +#define D11_AXI_BASE_ADDR 0xE8000000 +#define D11_SHM_BASE_ADDR (D11_AXI_BASE_ADDR + 0x4000) + +#define D11REG_ADDR(offset) (D11_BASE_ADDR + (offset)) +#define D11IHR_ADDR(offset) (D11_AXI_BASE_ADDR + 0x400 + (2 * (offset))) +#define D11SHM_ADDR(offset) (D11_SHM_BASE_ADDR + (offset)) + +/* MacControl register */ +#define D11_MACCONTROL_REG D11REG_ADDR(0x120) +#define D11_MACCONTROL_REG_WAKE 0x4000000 + +/* HUDI Sequence SHM bits */ +#define C_DS1_CTRL_SDIO_DS1_SLEEP 0x1 +#define C_DS1_CTRL_SDIO_MAC_ON 0x2 +#define C_DS1_CTRL_SDIO_RADIO_PHY_ON 0x4 +#define C_DS1_CTRL_SDIO_DS1_EXIT 0x8 +#define C_DS1_CTRL_PROC_DONE 0x100 +#define C_DS1_CTRL_REQ_VALID 0x200 + +/* M_ULP_WAKEIND bits */ +#define C_WATCHDOG_EXPIRY BIT(0) +#define C_FCBS_ERROR BIT(1) +#define C_RETX_FAILURE BIT(2) +#define C_HOST_WAKEUP BIT(3) +#define C_INVALID_FCBS_BLOCK BIT(4) +#define C_HUDI_DS1_EXIT BIT(5) +#define C_LOB_SLEEP BIT(6) +#define C_DS1_PHY_TXERR BIT(9) +#define C_DS1_WAKE_TIMER BIT(10) + +#define PHYTX_ERR_BLK_SIZE 18 +#define D11SHM_FIRST2BYTE_MASK 0xFFFF0000 +#define D11SHM_SECOND2BYTE_MASK 0x0000FFFF +#define D11SHM_2BYTE_SHIFT 16 + +#define D11SHM_RD(sdh, offset, ret) \ + brcmf_sdiod_readl(sdh, D11SHM_ADDR(offset), ret) + +/* SHM Read is motified based on SHM 4 byte alignment as SHM size is 2 bytes and + * 2 byte is currently not working on FMAC + * If SHM address is not 4 byte aligned, then right shift by 16 + * otherwise, mask the first two MSB bytes + * Suppose data in address 7260 is 0x440002 and it is 4 byte aligned + * Correct SHM value is 0x2 for this SHM offset and next SHM value is 0x44 + */ +#define D11SHM_RDW(sdh, offset, ret) \ + ((offset % 4) ? \ + (brcmf_sdiod_readl(sdh, D11SHM_ADDR(offset), ret) \ + >> D11SHM_2BYTE_SHIFT) : \ + (brcmf_sdiod_readl(sdh, D11SHM_ADDR(offset), ret) \ + & D11SHM_SECOND2BYTE_MASK)) + +/* SHM is of size 2 bytes, 4 bytes write will overwrite other SHM's + * First read 4 bytes and then clear the required two bytes based on + * 4 byte alignment, then update the required value and write the + * 4 byte value now + */ +#define D11SHM_WR(sdh, offset, val, mask, ret) \ + do { \ + if ((offset) % 4) \ + val = (val & D11SHM_SECOND2BYTE_MASK) | \ + ((mask) << D11SHM_2BYTE_SHIFT); \ + else \ + val = (mask) | (val & D11SHM_FIRST2BYTE_MASK); \ + brcmf_sdiod_writel(sdh, D11SHM_ADDR(offset), val, ret); \ + } while (0) +#define D11REG_WR(sdh, addr, val, ret) \ + brcmf_sdiod_writel(sdh, addr, val, ret) + +#define D11REG_RD(sdh, addr, ret) \ + brcmf_sdiod_readl(sdh, addr, ret) + #endif /* BRCMFMAC_SDIO_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/trxhdr.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/trxhdr.h new file mode 100644 index 0000000000000..0411c7c7ffb99 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/trxhdr.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: ISC */ +/* Copyright (c) 2020 Cypress Semiconductor Corporation */ + +#ifndef BRCMFMAC_TRXHDR_H +#define BRCMFMAC_TRXHDR_H + +/* Bootloader makes special use of trx header "offsets" array */ +enum { + TRX_OFFSET_SIGN_INFO_IDX = 0, + TRX_OFFSET_DATA_FOR_SIGN1_IDX = 1, + TRX_OFFSET_DATA_FOR_SIGN2_IDX = 2, + TRX_OFFSET_ROOT_MODULUS_IDX = 3, + TRX_OFFSET_ROOT_EXPONENT_IDX = 67, + TRX_OFFSET_CONT_MODULUS_IDX = 68, + TRX_OFFSET_CONT_EXPONENT_IDX = 132, + TRX_OFFSET_HASH_FW_IDX = 133, + TRX_OFFSET_FW_LEN_IDX = 149, + TRX_OFFSET_TR_RST_IDX = 150, + TRX_OFFSET_FW_VER_FOR_ANTIROOLBACK_IDX = 151, + TRX_OFFSET_IV_IDX = 152, + TRX_OFFSET_NONCE_IDX = 160, + TRX_OFFSET_SIGN_INFO2_IDX = 168, + TRX_OFFSET_MAX_IDX +}; + +#define TRX_MAGIC 0x30524448 /* "HDR0" */ +#define TRX_VERSION 4 /* Version 4 */ +#define TRX_MAX_OFFSET TRX_OFFSET_MAX_IDX /* Max number of file offsets */ + +struct trx_header_le { + __le32 magic; /* "HDR0" */ + __le32 len; /* Length of file including header */ + __le32 crc32; /* CRC from flag_version to end of file */ + __le32 flag_version; /* 0:15 flags, 16:31 version */ + __le32 offsets[TRX_MAX_OFFSET]; /* Offsets of partitions */ +}; + +#endif /* BRCMFMAC_TRXHDR_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c index 586f4dfc638b9..c23fa65180bde 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c @@ -19,6 +19,7 @@ #include "core.h" #include "common.h" #include "bcdc.h" +#include "cfg80211.h" #define IOCTL_RESP_TIMEOUT msecs_to_jiffies(2000) @@ -39,7 +40,7 @@ BRCMF_FW_DEF(43143, "brcmfmac43143"); BRCMF_FW_DEF(43236B, "brcmfmac43236b"); BRCMF_FW_DEF(43242A, "brcmfmac43242a"); BRCMF_FW_DEF(43569, "brcmfmac43569"); -BRCMF_FW_DEF(4373, "brcmfmac4373"); +CY_FW_DEF(4373, "cyfmac4373"); static const struct brcmf_firmware_mapping brcmf_usb_fwnames[] = { BRCMF_FW_ENTRY(BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, 43143), @@ -638,6 +639,10 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb) goto fail; } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) + if (devinfo->bus_pub.bus->allow_skborphan) + skb_orphan(skb); +#endif req->skb = skb; req->devinfo = devinfo; usb_fill_bulk_urb(req->urb, devinfo->usbdev, devinfo->tx_pipe, @@ -1219,8 +1224,14 @@ static void brcmf_usb_probe_phase2(struct device *dev, int ret, if (ret) goto error; + if (BRCMF_FWCON_ON()) { + ret = brcmf_fwlog_attach(devinfo->dev); + if (ret) + goto error; + } + /* Attach to the common driver interface */ - ret = brcmf_attach(devinfo->dev); + ret = brcmf_attach(devinfo->dev, true); if (ret) goto error; @@ -1279,6 +1290,9 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) bus->ops = &brcmf_usb_bus_ops; bus->proto_type = BRCMF_PROTO_BCDC; bus->always_use_fws_queue = true; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) + bus->allow_skborphan = true; +#endif #ifdef CONFIG_PM bus->wowl_supported = true; #endif @@ -1295,9 +1309,17 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) ret = brcmf_alloc(devinfo->dev, devinfo->settings); if (ret) goto fail; - ret = brcmf_attach(devinfo->dev); + + if (BRCMF_FWCON_ON()) { + ret = brcmf_fwlog_attach(devinfo->dev); + if (ret) + goto fail; + } + + ret = brcmf_attach(devinfo->dev, true); if (ret) goto fail; + /* we are done */ complete(&devinfo->dev_init_done); return 0; @@ -1480,8 +1502,22 @@ static int brcmf_usb_suspend(struct usb_interface *intf, pm_message_t state) { struct usb_device *usb = interface_to_usbdev(intf); struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev); + struct brcmf_bus *bus; + struct brcmf_cfg80211_info *config; + int retry = BRCMF_PM_WAIT_MAXRETRY; brcmf_dbg(USB, "Enter\n"); + + bus = devinfo->bus_pub.bus; + config = bus->drvr->config; + while (retry && + config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING) { + usleep_range(10000, 20000); + retry--; + } + if (!retry && config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING) + brcmf_err("timed out wait for cfg80211 suspended\n"); + devinfo->bus_pub.state = BRCMFMAC_USB_STATE_SLEEP; brcmf_cancel_all_urbs(devinfo); device_set_wakeup_enable(devinfo->dev, true); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c index d07e7c7355d9d..dec1307a0e006 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c @@ -64,6 +64,16 @@ static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy, *(char *)(dcmd_buf + len) = '\0'; } + if (cmdhdr->cmd == BRCMF_C_SET_AP) { + if (*(int *)(dcmd_buf) == 1) { + ifp->vif->wdev.iftype = NL80211_IFTYPE_AP; + brcmf_net_setcarrier(ifp, true); + } else { + ifp->vif->wdev.iftype = NL80211_IFTYPE_STATION; + } + brcmf_cfg80211_update_proto_addr_mode(&vif->wdev); + } + if (cmdhdr->set) ret = brcmf_fil_cmd_data_set(ifp, cmdhdr->cmd, dcmd_buf, ret_len); @@ -104,6 +114,110 @@ static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy, return ret; } +static int brcmf_cfg80211_vndr_cmds_int_get(struct brcmf_if *ifp, + u32 cmd, struct wiphy *wiphy) +{ + struct sk_buff *reply; + int get_value = 0; + int ret; + + ret = brcmf_fil_cmd_int_get(ifp, cmd, &get_value); + if (ret) + brcmf_err("Command %u get failure. Error : %d\n", cmd, ret); + + reply = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(int)); + nla_put_nohdr(reply, sizeof(int), &get_value); + ret = cfg80211_vendor_cmd_reply(reply); + if (ret) + brcmf_err("Command %u failure. Error : %d\n", cmd, ret); + return ret; +} + +static int brcmf_cfg80211_vndr_cmds_int_set(struct brcmf_if *ifp, int val, u32 cmd) +{ + int ret; + + ret = brcmf_fil_cmd_int_set(ifp, cmd, val); + if (ret < 0) + brcmf_err("Command %u set failure. Error : %d\n", cmd, ret); + return ret; +} + +static int brcmf_cfg80211_vndr_cmds_frameburst(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int len) +{ + int ret; + int val = *(int *)data; + struct brcmf_cfg80211_vif *vif; + struct brcmf_if *ifp; + + vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + ifp = vif->ifp; + + if (val == 0x0 || val == 0x1) + ret = brcmf_cfg80211_vndr_cmds_int_set(ifp, val, + BRCMF_C_SET_FAKEFRAG); + else if (val == 0xff) + ret = brcmf_cfg80211_vndr_cmds_int_get(ifp, + BRCMF_C_GET_FAKEFRAG, + wiphy); + else + brcmf_err("Invalid Input\n"); + + return ret; +} + +s32 +brcmf_wiphy_phy_temp_evt_handler(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) + +{ + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + struct wiphy *wiphy = cfg_to_wiphy(cfg); + struct sk_buff *skb; + struct nlattr *phy_temp_data; + u32 version, temp, tempdelta; + struct brcmf_phy_temp_evt *phy_temp_evt; + + phy_temp_evt = (struct brcmf_phy_temp_evt *)data; + + version = le32_to_cpu(phy_temp_evt->version); + temp = le32_to_cpu(phy_temp_evt->temp); + tempdelta = le32_to_cpu(phy_temp_evt->tempdelta); + + skb = cfg80211_vendor_event_alloc(wiphy, NULL, + sizeof(*phy_temp_evt), + BRCMF_VNDR_EVTS_PHY_TEMP, + GFP_KERNEL); + + if (!skb) { + brcmf_dbg(EVENT, "NO MEM: can't allocate skb for vendor PHY_TEMP_EVENT\n"); + return -ENOMEM; + } + + phy_temp_data = nla_nest_start(skb, NL80211_ATTR_VENDOR_EVENTS); + if (!phy_temp_data) { + nla_nest_cancel(skb, phy_temp_data); + kfree_skb(skb); + brcmf_dbg(EVENT, "skb could not nest vendor attributes\n"); + return -EMSGSIZE; + } + + if (nla_put_u32(skb, BRCMF_NLATTR_VERS, version) || + nla_put_u32(skb, BRCMF_NLATTR_PHY_TEMP, temp) || + nla_put_u32(skb, BRCMF_NLATTR_PHY_TEMPDELTA, tempdelta)) { + kfree_skb(skb); + brcmf_dbg(EVENT, "NO ROOM in skb for vendor PHY_TEMP_EVENT\n"); + return -EMSGSIZE; + } + + nla_nest_end(skb, phy_temp_data); + + cfg80211_vendor_event(skb, GFP_KERNEL); + return 0; +} + const struct wiphy_vendor_command brcmf_vendor_cmds[] = { { { @@ -115,4 +229,21 @@ const struct wiphy_vendor_command brcmf_vendor_cmds[] = { .policy = VENDOR_CMD_RAW_DATA, .doit = brcmf_cfg80211_vndr_cmds_dcmd_handler }, + { + { + .vendor_id = BROADCOM_OUI, + .subcmd = BRCMF_VNDR_CMDS_FRAMEBURST + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .policy = VENDOR_CMD_RAW_DATA, + .doit = brcmf_cfg80211_vndr_cmds_frameburst + }, +}; + +const struct nl80211_vendor_cmd_info brcmf_vendor_events[] = { + { + .vendor_id = BROADCOM_OUI, + .subcmd = BRCMF_VNDR_EVTS_PHY_TEMP, + }, }; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h index 418f33ea6fd3f..3c691bbe0baec 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h @@ -11,9 +11,15 @@ enum brcmf_vndr_cmds { BRCMF_VNDR_CMDS_UNSPEC, BRCMF_VNDR_CMDS_DCMD, + BRCMF_VNDR_CMDS_FRAMEBURST, BRCMF_VNDR_CMDS_LAST }; +enum brcmf_vndr_evts { + BRCMF_VNDR_EVTS_PHY_TEMP, + BRCMF_VNDR_EVTS_LAST +}; + /** * enum brcmf_nlattrs - nl80211 message attributes * @@ -25,11 +31,21 @@ enum brcmf_nlattrs { BRCMF_NLATTR_LEN, BRCMF_NLATTR_DATA, + BRCMF_NLATTR_VERS, + BRCMF_NLATTR_PHY_TEMP, + BRCMF_NLATTR_PHY_TEMPDELTA, __BRCMF_NLATTR_AFTER_LAST, BRCMF_NLATTR_MAX = __BRCMF_NLATTR_AFTER_LAST - 1 }; +/* structure of event sent up by firmware: is this the right place for it? */ +struct brcmf_phy_temp_evt { + __le32 version; + __le32 temp; + __le32 tempdelta; +} __packed; + /** * struct brcmf_vndr_dcmd_hdr - message header for cfg80211 vendor command dcmd * support @@ -49,5 +65,9 @@ struct brcmf_vndr_dcmd_hdr { }; extern const struct wiphy_vendor_command brcmf_vendor_cmds[]; +extern const struct nl80211_vendor_cmd_info brcmf_vendor_events[]; +s32 brcmf_wiphy_phy_temp_evt_handler(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, + void *data); #endif /* _vendor_h_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c b/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c index 1e2b1e487eb76..5effa27542e95 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c @@ -87,10 +87,20 @@ static void brcmu_d11ac_encchspec(struct brcmu_chan *ch) 0, d11ac_bw(ch->bw)); ch->chspec &= ~BRCMU_CHSPEC_D11AC_BND_MASK; - if (ch->chnum <= CH_MAX_2G_CHANNEL) - ch->chspec |= BRCMU_CHSPEC_D11AC_BND_2G; - else + switch (ch->band) { + case BRCMU_CHAN_BAND_6G: + ch->chspec |= BRCMU_CHSPEC_D11AC_BND_6G; + break; + case BRCMU_CHAN_BAND_5G: ch->chspec |= BRCMU_CHSPEC_D11AC_BND_5G; + break; + case BRCMU_CHAN_BAND_2G: + ch->chspec |= BRCMU_CHSPEC_D11AC_BND_2G; + break; + default: + WARN_ONCE(1, "Invalid band 0x%04x\n", ch->band); + break; + } } static void brcmu_d11n_decchspec(struct brcmu_chan *ch) @@ -222,6 +232,9 @@ static void brcmu_d11ac_decchspec(struct brcmu_chan *ch) } switch (ch->chspec & BRCMU_CHSPEC_D11AC_BND_MASK) { + case BRCMU_CHSPEC_D11AC_BND_6G: + ch->band = BRCMU_CHAN_BAND_6G; + break; case BRCMU_CHSPEC_D11AC_BND_5G: ch->band = BRCMU_CHAN_BAND_5G; break; diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h index c6c4be05159d4..c6d9aad29c7ba 100644 --- a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h @@ -14,6 +14,7 @@ #define BRCM_USB_VENDOR_ID_LINKSYS 0x13b1 #define CY_USB_VENDOR_ID_CYPRESS 0x04b4 #define BRCM_PCIE_VENDOR_ID_BROADCOM PCI_VENDOR_ID_BROADCOM +#define CY_PCIE_VENDOR_ID_CYPRESS 0x12be /* Chipcommon Core Chip IDs */ #define BRCM_CC_43143_CHIP_ID 43143 @@ -49,8 +50,12 @@ #define BRCM_CC_4366_CHIP_ID 0x4366 #define BRCM_CC_43664_CHIP_ID 43664 #define BRCM_CC_4371_CHIP_ID 0x4371 +#define CY_CC_43430_CHIP_ID 43430 +#define CY_CC_43439_CHIP_ID 43439 #define CY_CC_4373_CHIP_ID 0x4373 #define CY_CC_43012_CHIP_ID 43012 +#define CY_CC_89459_CHIP_ID 0x4355 +#define CY_CC_55572_CHIP_ID 0xd908 /* USB Device IDs */ #define BRCM_USB_43143_DEVICE_ID 0xbd1e @@ -69,6 +74,7 @@ #define BRCM_PCIE_4356_DEVICE_ID 0x43ec #define BRCM_PCIE_43567_DEVICE_ID 0x43d3 #define BRCM_PCIE_43570_DEVICE_ID 0x43d9 +#define BRCM_PCIE_43570_RAW_DEVICE_ID 0xaa31 #define BRCM_PCIE_4358_DEVICE_ID 0x43e9 #define BRCM_PCIE_4359_DEVICE_ID 0x43ef #define BRCM_PCIE_43602_DEVICE_ID 0x43ba @@ -83,7 +89,16 @@ #define BRCM_PCIE_4366_2G_DEVICE_ID 0x43c4 #define BRCM_PCIE_4366_5G_DEVICE_ID 0x43c5 #define BRCM_PCIE_4371_DEVICE_ID 0x440d - +#define CY_PCIE_89459_DEVICE_ID 0x4415 +#define CY_PCIE_89459_RAW_DEVICE_ID 0x4355 +#define CY_PCIE_54591_DEVICE_ID 0x4417 +#define CY_PCIE_54590_DEVICE_ID 0x4416 +#define CY_PCIE_54594_DEVICE_ID 0x441a +#define CY_PCIE_55572_DEVICE_ID 0xbd31 +#define CY_PCIE_4373_RAW_DEVICE_ID 0x4373 +#define CY_PCIE_4373_DUAL_DEVICE_ID 0x4418 +#define CY_PCIE_4373_2G_DEVICE_ID 0x4419 +#define CY_PCIE_4373_5G_DEVICE_ID 0x441a /* brcmsmac IDs */ #define BCM4313_D11N2G_ID 0x4727 /* 4313 802.11n 2.4G device */ diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_d11.h b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_d11.h index f6344023855c3..3d7655c9c0586 100644 --- a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_d11.h +++ b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_d11.h @@ -82,11 +82,18 @@ #define BRCMU_CHSPEC_D11AC_BND_SHIFT 14 #define BRCMU_CHSPEC_D11AC_BND_2G 0x0000 #define BRCMU_CHSPEC_D11AC_BND_3G 0x4000 -#define BRCMU_CHSPEC_D11AC_BND_4G 0x8000 +#define BRCMU_CHSPEC_D11AC_BND_6G 0x8000 #define BRCMU_CHSPEC_D11AC_BND_5G 0xc000 - -#define BRCMU_CHAN_BAND_2G 0 -#define BRCMU_CHAN_BAND_5G 1 +#define BRCMU_CHSPEC_IS5G(chspec) \ + (((chspec) & BRCMU_CHSPEC_D11AC_BND_MASK) == BRCMU_CHSPEC_D11AC_BND_5G) +#define BRCMU_CHSPEC_IS6G(chspec) \ + (((chspec) & BRCMU_CHSPEC_D11AC_BND_MASK) == BRCMU_CHSPEC_D11AC_BND_6G) +#define BRCMU_CHAN_BAND_2G 1 +#define BRCMU_CHAN_BAND_5G 2 +#define BRCMU_CHAN_BAND_6G 3 +#define BRCMU_CHAN_BAND_TO_NL80211(band) \ + ((band) == BRCMU_CHAN_BAND_2G ? NL80211_BAND_2GHZ : \ + ((band) == BRCMU_CHAN_BAND_5G ? NL80211_BAND_5GHZ : NL80211_BAND_6GHZ)) enum brcmu_chan_bw { BRCMU_CHAN_BW_20, diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_utils.h b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_utils.h index 9465323286673..48791ac87496d 100644 --- a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_utils.h +++ b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_utils.h @@ -21,6 +21,19 @@ } \ } +/* Spin at most 'ms' milliseconds with polling interval 'interval' milliseconds + * while 'exp' is true. Caller should explicitly test 'exp' when this completes + * and take appropriate error action if 'exp' is still true. + */ +#define SPINWAIT_MS(exp, ms, interval) { \ + typeof(interval) interval_ = (interval); \ + uint countdown = (ms) + (interval_ - 1U); \ + while ((exp) && (countdown >= interval_)) { \ + msleep(interval_); \ + countdown -= interval_; \ + } \ +} + /* osl multi-precedence packet queue */ #define PKTQ_LEN_DEFAULT 128 /* Max 128 packets */ #define PKTQ_MAX_PREC 16 /* Maximum precedence levels */ diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h index 7552bdb91991c..04bf1c0b3b06f 100644 --- a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h +++ b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h @@ -92,7 +92,8 @@ #define WLC_BAND_AUTO 0 /* auto-select */ #define WLC_BAND_5G 1 /* 5 Ghz */ #define WLC_BAND_2G 2 /* 2.4 Ghz */ -#define WLC_BAND_ALL 3 /* all bands */ +#define WLC_BAND_6G 3 /* 6 Ghz */ +#define WLC_BAND_ALL 4 /* all bands */ #define CHSPEC_CHANNEL(chspec) ((u8)((chspec) & WL_CHANSPEC_CHAN_MASK)) #define CHSPEC_BAND(chspec) ((chspec) & WL_CHANSPEC_BAND_MASK) @@ -201,6 +202,13 @@ static inline bool ac_bitmap_tst(u8 bitmap, int prec) #define CRYPTO_ALGO_AES_RESERVED2 6 #define CRYPTO_ALGO_NALG 7 +#define CRYPTO_ALGO_AES_GCM 14 /* 128 bit GCM */ +#define CRYPTO_ALGO_AES_CCM256 15 /* 256 bit CCM */ +#define CRYPTO_ALGO_AES_GCM256 16 /* 256 bit GCM */ +#define CRYPTO_ALGO_BIP_CMAC256 17 /* 256 bit BIP CMAC */ +#define CRYPTO_ALGO_BIP_GMAC 18 /* 128 bit BIP GMAC */ +#define CRYPTO_ALGO_BIP_GMAC256 19 /* 256 bit BIP GMAC */ + /* wireless security bitvec */ #define WEP_ENABLED 0x0001 @@ -233,6 +241,13 @@ static inline bool ac_bitmap_tst(u8 bitmap, int prec) #define WPA3_AUTH_SAE_PSK 0x40000 /* SAE with 4-way handshake */ +#define WFA_AUTH_DPP 0x200000 /* WFA DPP AUTH */ +#define WPA3_AUTH_1X_SUITE_B_SHA384 0x400000 /* Suite B-192 SHA384 */ + + +#define WFA_OUI "\x50\x6F\x9A" /* WFA OUI */ +#define DPP_VER 0x1A /* WFA DPP v1.0 */ + #define DOT11_DEFAULT_RTS_LEN 2347 #define DOT11_DEFAULT_FRAG_LEN 2346 diff --git a/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h b/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h index 0340bba968688..39cd34c226281 100644 --- a/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h +++ b/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h @@ -214,8 +214,197 @@ struct chipcregs { u32 PAD[3]; u32 retention_grpidx; /* 0x680 */ u32 retention_grpctl; /* 0x684 */ - u32 PAD[94]; - u16 sromotp[768]; + u32 mac_res_req_timer; /* 0x688 */ + u32 mac_res_req_mask; /* 0x68c */ + u32 PAD[18]; + u32 pmucontrol_ext; /* 0x6d8 */ + u32 slowclkperiod; /* 0x6dc */ + u32 PAD[8]; + u32 pmuintmask0; /* 0x700 */ + u32 pmuintmask1; /* 0x704 */ + u32 PAD[14]; + u32 pmuintstatus; /* 0x740 */ + u32 extwakeupstatus; /* 0x744 */ + u32 watchdog_res_mask; /* 0x748 */ + u32 swscratch; /* 0x750 */ + u32 PAD[3]; + u32 extwakemask[2]; /* 0x760-0x764 */ + u32 PAD[2]; + u32 extwakereqmask[2]; /* 0x770-0x774 */ + u32 PAD[2]; + u32 pmuintctrl0; /* 0x780 */ + u32 pmuintctrl1; /* 0x784 */ + u32 PAD[2]; + u32 extwakectrl[2]; /* 0x790 */ +}; + +#define CHIPGCIREGOFFS(field) offsetof(struct chipgciregs, field) + +struct chipgciregs { + u32 gci_corecaps0; /* 0x000 */ + u32 gci_corecaps1; /* 0x004 */ + u32 gci_corecaps2; /* 0x008 */ + u32 gci_corectrl; /* 0x00c */ + u32 gci_corestat; /* 0x010 */ + u32 gci_intstat; /* 0x014 */ + u32 gci_intmask; /* 0x018 */ + u32 gci_wakemask; /* 0x01c */ + u32 gci_levelintstat; /* 0x020 */ + u32 gci_eventintstat; /* 0x024 */ + u32 gci_wakelevelintstat; /* 0x028 */ + u32 gci_wakeeventintstat; /* 0x02c */ + u32 semaphoreintstatus; /* 0x030 */ + u32 semaphoreintmask; /* 0x034 */ + u32 semaphorerequest; /* 0x038 */ + u32 semaphorereserve; /* 0x03c */ + u32 gci_indirect_addr; /* 0x040 */ + u32 gci_gpioctl; /* 0x044 */ + u32 gci_gpiostatus; /* 0x048 */ + u32 gci_gpiomask; /* 0x04c */ + u32 eventsummary; /* 0x050 */ + u32 gci_miscctl; /* 0x054 */ + u32 gci_gpiointmask; /* 0x058 */ + u32 gci_gpiowakemask; /* 0x05c */ + u32 gci_input[32]; /* 0x060 */ + u32 gci_event[32]; /* 0x0e0 */ + u32 gci_output[4]; /* 0x160 */ + u32 gci_control_0; /* 0x170 */ + u32 gci_control_1; /* 0x174 */ + u32 gci_intpolreg; /* 0x178 */ + u32 gci_levelintmask; /* 0x17c */ + u32 gci_eventintmask; /* 0x180 */ + u32 wakelevelintmask; /* 0x184 */ + u32 wakeeventintmask; /* 0x188 */ + u32 hwmask; /* 0x18c */ + u32 PAD; + u32 gci_inbandeventintmask; /* 0x194 */ + u32 PAD; + u32 gci_inbandeventstatus; /* 0x19c */ + u32 gci_seciauxtx; /* 0x1a0 */ + u32 gci_seciauxrx; /* 0x1a4 */ + u32 gci_secitx_datatag; /* 0x1a8 */ + u32 gci_secirx_datatag; /* 0x1ac */ + u32 gci_secitx_datamask; /* 0x1b0 */ + u32 gci_seciusef0tx_reg; /* 0x1b4 */ + u32 gci_secif0tx_offset; /* 0x1b8 */ + u32 gci_secif0rx_offset; /* 0x1bc */ + u32 gci_secif1tx_offset; /* 0x1c0 */ + u32 gci_rxfifo_common_ctrl; /* 0x1c4 */ + u32 gci_rxfifoctrl; /* 0x1c8 */ + u32 gci_hw_sema_status; /* 0x1cc */ + u32 gci_seciuartescval; /* 0x1d0 */ + u32 gic_seciuartautobaudctr; /* 0x1d4 */ + u32 gci_secififolevel; /* 0x1d8 */ + u32 gci_seciuartdata; /* 0x1dc */ + u32 gci_secibauddiv; /* 0x1e0 */ + u32 gci_secifcr; /* 0x1e4 */ + u32 gci_secilcr; /* 0x1e8 */ + u32 gci_secimcr; /* 0x1ec */ + u32 gci_secilsr; /* 0x1f0 */ + u32 gci_secimsr; /* 0x1f4 */ + u32 gci_baudadj; /* 0x1f8 */ + u32 gci_inbandintmask; /* 0x1fc */ + u32 gci_chipctrl; /* 0x200 */ + u32 gci_chipsts; /* 0x204 */ + u32 gci_gpioout; /* 0x208 */ + u32 gci_gpioout_read; /* 0x20C */ + u32 gci_mpwaketx; /* 0x210 */ + u32 gci_mpwakedetect; /* 0x214 */ + u32 gci_seciin_ctrl; /* 0x218 */ + u32 gci_seciout_ctrl; /* 0x21C */ + u32 gci_seciin_auxfifo_en; /* 0x220 */ + u32 gci_seciout_txen_txbr; /* 0x224 */ + u32 gci_seciin_rxbrstatus; /* 0x228 */ + u32 gci_seciin_rxerrstatus; /* 0x22C */ + u32 gci_seciin_fcstatus; /* 0x230 */ + u32 gci_seciout_txstatus; /* 0x234 */ + u32 gci_seciout_txbrstatus; /* 0x238 */ + u32 wlan_mem_info; /* 0x23C */ + u32 wlan_bankxinfo; /* 0x240 */ + u32 bt_smem_select; /* 0x244 */ + u32 bt_smem_stby; /* 0x248 */ + u32 bt_smem_status; /* 0x24C */ + u32 wlan_bankxactivepda; /* 0x250 */ + u32 wlan_bankxsleeppda; /* 0x254 */ + u32 wlan_bankxkill; /* 0x258 */ + u32 PAD[41]; + u32 gci_chipid; /* 0x300 */ + u32 PAD[3]; + u32 otpstatus; /* 0x310 */ + u32 otpcontrol; /* 0x314 */ + u32 otpprog; /* 0x318 */ + u32 otplayout; /* 0x31c */ + u32 otplayoutextension; /* 0x320 */ + u32 otpcontrol1; /* 0x324 */ + u32 otpprogdata; /* 0x328 */ + u32 PAD[52]; + u32 otpECCstatus; /* 0x3FC */ + u32 PAD[512]; + u32 lhl_core_capab_adr; /* 0xC00 */ + u32 lhl_main_ctl_adr; /* 0xC04 */ + u32 lhl_pmu_ctl_adr; /* 0xC08 */ + u32 lhl_extlpo_ctl_adr; /* 0xC0C */ + u32 lpo_ctl_adr; /* 0xC10 */ + u32 lhl_lpo2_ctl_adr; /* 0xC14 */ + u32 lhl_osc32k_ctl_adr; /* 0xC18 */ + u32 lhl_clk_status_adr; /* 0xC1C */ + u32 lhl_clk_det_ctl_adr; /* 0xC20 */ + u32 lhl_clk_sel_adr; /* 0xC24 */ + u32 hidoff_cnt_adr[2]; /* 0xC28-0xC2C */ + u32 lhl_autoclk_ctl_adr; /* 0xC30 */ + u32 PAD; + u32 lhl_hibtim_adr; /* 0xC38 */ + u32 lhl_wl_ilp_val_adr; /* 0xC3C */ + u32 lhl_wl_armtim0_intrp_adr; /* 0xC40 */ + u32 lhl_wl_armtim0_st_adr; /* 0xC44 */ + u32 lhl_wl_armtim0_adr; /* 0xC48 */ + u32 PAD[9]; + u32 lhl_wl_mactim0_intrp_adr; /* 0xC70 */ + u32 lhl_wl_mactim0_st_adr; /* 0xC74 */ + u32 lhl_wl_mactim_int0_adr; /* 0xC78 */ + u32 lhl_wl_mactim_frac0_adr; /* 0xC7C */ + u32 lhl_wl_mactim1_intrp_adr; /* 0xC80 */ + u32 lhl_wl_mactim1_st_adr; /* 0xC84 */ + u32 lhl_wl_mactim_int1_adr; /* 0xC88 */ + u32 lhl_wl_mactim_frac1_adr; /* 0xC8C */ + u32 PAD[8]; + u32 gpio_int_en_port_adr[4]; /* 0xCB0-0xCBC */ + u32 gpio_int_st_port_adr[4]; /* 0xCC0-0xCCC */ + u32 gpio_ctrl_iocfg_p_adr[64]; /* 0xCD0-0xDCC */ + u32 gpio_gctrl_iocfg_p0_p39_adr; /* 0xDD0 */ + u32 gpio_gdsctrl_iocfg_p0_p25_p30_p39_adr; /* 0xDD4 */ + u32 gpio_gdsctrl_iocfg_p26_p29_adr; /* 0xDD8 */ + u32 PAD[8]; + u32 lhl_gpio_din0_adr; /* 0xDFC */ + u32 lhl_gpio_din1_adr; /* 0xE00 */ + u32 lhl_wkup_status_adr; /* 0xE04 */ + u32 lhl_ctl_adr; /* 0xE08 */ + u32 lhl_adc_ctl_adr; /* 0xE0C */ + u32 lhl_qdxyz_in_dly_adr; /* 0xE10 */ + u32 lhl_optctl_adr; /* 0xE14 */ + u32 lhl_optct2_adr; /* 0xE18 */ + u32 lhl_scanp_cntr_init_val_adr; /* 0xE1C */ + u32 lhl_opt_togg_val_adr[6]; /* 0xE20-0xE34 */ + u32 lhl_optx_smp_val_adr; /* 0xE38 */ + u32 lhl_opty_smp_val_adr; /* 0xE3C */ + u32 lhl_optz_smp_val_adr; /* 0xE40 */ + u32 lhl_hidoff_keepstate_adr[3]; /* 0xE44-0xE4C */ + u32 lhl_bt_slmboot_ctl0_adr[4]; /* 0xE50-0xE5C */ + u32 lhl_wl_fw_ctl; /* 0xE60 */ + u32 lhl_wl_hw_ctl_adr[2]; /* 0xE64-0xE68 */ + u32 lhl_bt_hw_ctl_adr; /* 0xE6C */ + u32 lhl_top_pwrseq_en_adr; /* 0xE70 */ + u32 lhl_top_pwrdn_ctl_adr; /* 0xE74 */ + u32 lhl_top_pwrup_ctl_adr; /* 0xE78 */ + u32 lhl_top_pwrseq_ctl_adr; /* 0xE7C */ + u32 lhl_top_pwrdn2_ctl_adr; /* 0xE80 */ + u32 lhl_top_pwrup2_ctl_adr; /* 0xE84 */ + u32 wpt_regon_intrp_cfg_adr; /* 0xE88 */ + u32 bt_regon_intrp_cfg_adr; /* 0xE8C */ + u32 wl_regon_intrp_cfg_adr; /* 0xE90 */ + u32 regon_intrp_st_adr; /* 0xE94 */ + u32 regon_intrp_en_adr; /* 0xE98 */ + }; /* chipid */ @@ -308,4 +497,6 @@ struct chipcregs { */ #define PMU_MAX_TRANSITION_DLY 15000 +#define DEFAULT_43012_MIN_RES_MASK 0x0f8bfe77 + #endif /* _SBCHIPC_H */ diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h index 12036619346cf..6afd50fef4da4 100644 --- a/include/linux/mmc/sdio_ids.h +++ b/include/linux/mmc/sdio_ids.h @@ -63,9 +63,10 @@ #define SDIO_DEVICE_ID_BROADCOM_4339 0x4339 #define SDIO_DEVICE_ID_BROADCOM_4345 0x4345 #define SDIO_DEVICE_ID_BROADCOM_4354 0x4354 -#define SDIO_DEVICE_ID_BROADCOM_CYPRESS_89359 0x4355 +#define SDIO_DEVICE_ID_BROADCOM_CYPRESS_89459 0x4355 #define SDIO_DEVICE_ID_BROADCOM_4356 0x4356 #define SDIO_DEVICE_ID_BROADCOM_4359 0x4359 +#define SDIO_DEVICE_ID_BROADCOM_CYPRESS_43439 0xa9af #define SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373 0x4373 #define SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012 0xa804 #define SDIO_DEVICE_ID_BROADCOM_43143 0xa887 @@ -76,6 +77,13 @@ #define SDIO_DEVICE_ID_BROADCOM_43430 0xa9a6 #define SDIO_DEVICE_ID_BROADCOM_43455 0xa9bf +#define SDIO_VENDOR_ID_CYPRESS 0x04b4 +#define SDIO_DEVICE_ID_CYPRESS_54590 0xbd3a +#define SDIO_DEVICE_ID_CYPRESS_54591 0xbd3b +#define SDIO_DEVICE_ID_CYPRESS_54594 0xbd3c +#define SDIO_DEVICE_ID_CYPRESS_43439 0xbd3d +#define SDIO_DEVICE_ID_CYPRESS_55572 0xbd31 + #define SDIO_VENDOR_ID_MARVELL 0x02df #define SDIO_DEVICE_ID_MARVELL_LIBERTAS 0x9103 #define SDIO_DEVICE_ID_MARVELL_8688_WLAN 0x9104 diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index d5ab8d99739f1..fe3a4f77bfae2 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -6845,6 +6845,8 @@ struct cfg80211_fils_resp_params { * not known. This value is used only if @status < 0 to indicate that the * failure is due to a timeout and not due to explicit rejection by the AP. * This value is ignored in other cases (@status >= 0). + * @authorized: Indicates whether the connection is ready to transport + * data packets. */ struct cfg80211_connect_resp_params { int status; @@ -6856,6 +6858,7 @@ struct cfg80211_connect_resp_params { size_t resp_ie_len; struct cfg80211_fils_resp_params fils; enum nl80211_timeout_reason timeout_reason; + bool authorized; }; /** @@ -7005,6 +7008,9 @@ cfg80211_connect_timeout(struct net_device *dev, const u8 *bssid, * @resp_ie: association response IEs (may be %NULL) * @resp_ie_len: assoc response IEs length * @fils: FILS related roaming information. + * @authorized: true if the 802.1X authentication was done by the driver or is + * not needed (e.g., when Fast Transition protocol was used), false + * otherwise. Ignored for networks that don't use 802.1X authentication. */ struct cfg80211_roam_info { struct ieee80211_channel *channel; @@ -7015,6 +7021,7 @@ struct cfg80211_roam_info { const u8 *resp_ie; size_t resp_ie_len; struct cfg80211_fils_resp_params fils; + bool authorized; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 47700a2b9af96..402e6aedf1b54 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2384,7 +2384,10 @@ enum nl80211_commands { * in %NL80211_CMD_CONNECT to indicate that for 802.1X authentication it * wants to use the supported offload of the 4-way handshake. * @NL80211_ATTR_PMKR0_NAME: PMK-R0 Name for offloaded FT. - * @NL80211_ATTR_PORT_AUTHORIZED: (reserved) + * @NL80211_ATTR_PORT_AUTHORIZED: flag attribute used in %NL80211_CMD_ROAMED + * notification indicating that that 802.1X authentication was done by + * the driver or is not needed (because roaming used the Fast Transition + * protocol). * * @NL80211_ATTR_EXTERNAL_AUTH_ACTION: Identify the requested external * authentication operation (u32 attribute with an @@ -5956,6 +5959,7 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_SAE_OFFLOAD_AP, NL80211_EXT_FEATURE_FILS_DISCOVERY, NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP, + NL80211_EXT_FEATURE_ROAM_OFFLOAD, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/wireless/Makefile b/net/wireless/Makefile index 2eee93985ab0d..af590ae606b69 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -28,7 +28,7 @@ $(obj)/shipped-certs.c: $(wildcard $(srctree)/$(src)/certs/*.hex) @$(kecho) " GEN $@" @(echo '#include "reg.h"'; \ echo 'const u8 shipped_regdb_certs[] = {'; \ - cat $^ ; \ + echo | cat - $^ ; \ echo '};'; \ echo 'unsigned int shipped_regdb_certs_len = sizeof(shipped_regdb_certs);'; \ ) > $@ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 535e34a84d651..d5593b65e1ece 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -14090,7 +14090,9 @@ static int nl80211_set_pmk(struct sk_buff *skb, struct genl_info *info) return -EOPNOTSUPP; if (!wiphy_ext_feature_isset(&rdev->wiphy, - NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X)) + NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X) && + !wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_ROAM_OFFLOAD)) return -EOPNOTSUPP; if (!info->attrs[NL80211_ATTR_MAC] || !info->attrs[NL80211_ATTR_PMK]) @@ -16046,6 +16048,8 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, (nla_put_flag(msg, NL80211_ATTR_TIMED_OUT) || nla_put_u32(msg, NL80211_ATTR_TIMEOUT_REASON, cr->timeout_reason))) || + (cr->authorized && + nla_put_flag(msg, NL80211_ATTR_PORT_AUTHORIZED)) || (cr->req_ie && nla_put(msg, NL80211_ATTR_REQ_IE, cr->req_ie_len, cr->req_ie)) || (cr->resp_ie && @@ -16112,7 +16116,9 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev, (info->fils.pmk && nla_put(msg, NL80211_ATTR_PMK, info->fils.pmk_len, info->fils.pmk)) || (info->fils.pmkid && - nla_put(msg, NL80211_ATTR_PMKID, WLAN_PMKID_LEN, info->fils.pmkid))) + nla_put(msg, NL80211_ATTR_PMKID, WLAN_PMKID_LEN, info->fils.pmkid)) || + (info->authorized && + nla_put_flag(msg, NL80211_ATTR_PORT_AUTHORIZED))) goto nla_put_failure; genlmsg_end(msg, hdr); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 38df713f2e2ed..a7dc5df5d3683 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -888,6 +888,7 @@ void cfg80211_connect_done(struct net_device *dev, ev->cr.bss = params->bss; ev->cr.status = params->status; ev->cr.timeout_reason = params->timeout_reason; + ev->cr.authorized = params->authorized; spin_lock_irqsave(&wdev->event_lock, flags); list_add_tail(&ev->list, &wdev->event_list); @@ -1022,6 +1023,7 @@ void cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info, if (info->fils.update_erp_next_seq_num) ev->rm.fils.erp_next_seq_num = info->fils.erp_next_seq_num; ev->rm.bss = info->bss; + ev->rm.authorized = info->authorized; spin_lock_irqsave(&wdev->event_lock, flags); list_add_tail(&ev->list, &wdev->event_list); From c6cdc153417c2da25ece8470e8a36ea0f7c1e953 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Tue, 7 Sep 2021 17:28:46 -0500 Subject: [PATCH 24/65] Revert "kthread: Do not preempt current task if it is going to call schedule()" This reverts commit 26c7295be0c5e6da3fa45970e9748be983175b1b. Signed-off-by: Robert Nelson --- kernel/kthread.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/kernel/kthread.c b/kernel/kthread.c index 508fe52782857..05816e2771b67 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -243,15 +243,8 @@ static void __kthread_parkme(struct kthread *self) if (!test_bit(KTHREAD_SHOULD_PARK, &self->flags)) break; - /* - * Thread is going to call schedule(), do not preempt it, - * or the caller of kthread_park() may spend more time in - * wait_task_inactive(). - */ - preempt_disable(); complete(&self->parked); - schedule_preempt_disabled(); - preempt_enable(); + schedule(); } __set_current_state(TASK_RUNNING); } @@ -297,14 +290,8 @@ static int kthread(void *_create) /* OK, tell user we're spawned, wait for stop or wakeup */ __set_current_state(TASK_UNINTERRUPTIBLE); create->result = current; - /* - * Thread is going to call schedule(), do not preempt it, - * or the creator may spend more time in wait_task_inactive(). - */ - preempt_disable(); complete(done); - schedule_preempt_disabled(); - preempt_enable(); + schedule(); ret = -EINTR; if (!test_bit(KTHREAD_SHOULD_STOP, &self->flags)) { From c23027ae03461881339a9ad95bf90c9691e44fd9 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Tue, 24 Aug 2021 21:20:42 -0500 Subject: [PATCH 25/65] Overlays: Port RPi Overlay building Signed-off-by: Robert Nelson --- arch/arm/boot/dts/Makefile | 20 ++++++++------------ scripts/Makefile.dtbinst | 4 ++-- scripts/Makefile.lib | 23 ++++++++++++++++++----- 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index bf9a58291b588..267c93b55566b 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -1,4 +1,9 @@ # SPDX-License-Identifier: GPL-2.0 + +ifeq ($(CONFIG_OF_OVERLAY),y) +DTC_FLAGS += -@ +endif + dtb-$(CONFIG_ARCH_ALPINE) += \ alpine-db.dtb dtb-$(CONFIG_MACH_ARTPEC6) += \ @@ -881,10 +886,6 @@ dtb-$(CONFIG_SOC_DRA7XX) += \ dra72-evm-revc.dtb \ dra71-evm.dtb \ dra76-evm.dtb -dtb-merge-$(CONFIG_SOC_DRA7XX) += \ - am57xx-evm.dtb \ - am57xx-evm-reva3.dtb \ - dra71-evm-nand.dtb dtb-$(CONFIG_ARCH_ORION5X) += \ orion5x-kuroboxpro.dtb \ orion5x-lacie-d2-network.dtb \ @@ -1433,12 +1434,7 @@ dtb-$(CONFIG_ARCH_ASPEED) += \ aspeed-bmc-portwell-neptune.dtb \ aspeed-bmc-quanta-q71l.dtb -always-y += $(dtb-merge-y) - -$(addprefix $(obj)/,$(dtb-merge-y)): TI_DTBOS - @$(srctree)/scripts/dtb-merge $(srctree) $(objtree) $@ $(objtree)/scripts/dtc/fdtoverlay $(src)/ti - -TI_DTBOS: - $(Q)$(MAKE) $(build)=$(src)/ti +targets += dtbs dtbs_install +targets += $(dtb-y) -PHONY += TI_DTBOS +subdir-y := overlays diff --git a/scripts/Makefile.dtbinst b/scripts/Makefile.dtbinst index c6cb9a7e6ba39..079b833080110 100644 --- a/scripts/Makefile.dtbinst +++ b/scripts/Makefile.dtbinst @@ -18,10 +18,10 @@ include scripts/Kbuild.include include $(src)/Makefile dtbs := $(addprefix $(dst)/, $(dtb-y) $(if $(CONFIG_OF_ALL_DTBS),$(dtb-))) -dtbs += $(addprefix $(dst)/, $(dtb-merge-y) $(if $(CONFIG_OF_ALL_DTBS),$(dtb-merge-))) +dtbos := $(addprefix $(dst)/, $(dtbo-y) $(if $(CONFIG_OF_ALL_DTBS),$(dtb-))) subdirs := $(addprefix $(obj)/, $(subdir-y) $(subdir-m)) -__dtbs_install: $(dtbs) $(subdirs) +__dtbs_install: $(dtbs) $(dtbos) $(subdirs) @: quiet_cmd_dtb_install = INSTALL $@ diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 3c62e76701441..fc5630cb12f6f 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -86,9 +86,7 @@ extra-$(CONFIG_OF_ALL_DTBS) += $(dtb-) ifneq ($(CHECK_DTBS),) extra-y += $(patsubst %.dtb,%.dt.yaml, $(dtb-y)) -extra-y += $(patsubst %.dtbo,%.dt.yaml, $(dtb-y)) extra-$(CONFIG_OF_ALL_DTBS) += $(patsubst %.dtb,%.dt.yaml, $(dtb-)) -extra-$(CONFIG_OF_ALL_DTBS) += $(patsubst %.dtbo,%.dt.yaml, $(dtb-)) endif # Add subdir path @@ -327,9 +325,6 @@ cmd_dtc = $(HOSTCC) -E $(dtc_cpp_flags) -x assembler-with-cpp -o $(dtc-tmp) $< ; $(obj)/%.dtb: $(src)/%.dts $(DTC) FORCE $(call if_changed_dep,dtc) -$(obj)/%.dtbo: $(src)/%.dts $(DTC) FORCE - $(call if_changed_dep,dtc) - DT_CHECKER ?= dt-validate DT_BINDING_DIR := Documentation/devicetree/bindings # DT_TMP_SCHEMA may be overridden from Documentation/devicetree/bindings/Makefile @@ -346,6 +341,24 @@ endef $(obj)/%.dt.yaml: $(src)/%.dts $(DTC) $(DT_TMP_SCHEMA) FORCE $(call if_changed_rule,dtc,yaml) +quiet_cmd_dtco = DTCO $@ +cmd_dtco = mkdir -p $(dir ${dtc-tmp}) ; \ + $(CPP) $(dtc_cpp_flags) -x assembler-with-cpp -o $(dtc-tmp) $< ; \ + $(DTC) -@ -H epapr -O dtb -o $@ -b 0 \ + -i $(dir $<) $(DTC_FLAGS) \ + -Wno-interrupts_property \ + -Wno-label_is_string \ + -Wno-reg_format \ + -Wno-pci_device_bus_num \ + -Wno-i2c_bus_reg \ + -Wno-spi_bus_reg \ + -Wno-avoid_default_addr_size \ + -d $(depfile).dtc.tmp $(dtc-tmp) ; \ + cat $(depfile).pre.tmp $(depfile).dtc.tmp > $(depfile) + +$(obj)/%.dtbo: $(src)/%.dts FORCE + $(call if_changed_dep,dtco) + dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp) # Bzip2 From 8f8be2ec81978bed519a0cf107a91b0a7ec77e37 Mon Sep 17 00:00:00 2001 From: Pantelis Antoniou Date: Wed, 4 Dec 2013 19:32:00 +0200 Subject: [PATCH 26/65] OF: DT-Overlay configfs interface (v7) Add a runtime interface to using configfs for generic device tree overlay usage. With it its possible to use device tree overlays without having to use a per-platform overlay manager. Please see Documentation/devicetree/configfs-overlays.txt for more info. Changes since v6: - Default groups properties API changed. Changes since v5: - New style configfs. Changes since v4: - Loading fix for multiple overlays as found out by Geert Uytterhoeven Changes since v3: - Fixed compilation on SPARC & Xtensa Changes since v2: - Removed ifdef CONFIG_OF_OVERLAY (since for now it's required) - Created a documentation entry - Slight rewording in Kconfig Changes since v1: - of_resolve() -> of_resolve_phandles(). Signed-off-by: Pantelis Antoniou [geert: Use %zu to format size_t] [geert: Rebase to v4.15-rc1] [geert: Make cfs_overlay_item_dtbo_{read,write}() and of_cfs_overlay_group static] [geert: Let OF_CONFIGFS select OF_FLATTREE to fix sparc all*config] [geert: Spelling/grammar s/rationalle of/rationale for/] [geert: Rebase on top of commit 39a751a4cb7e4798 ("of: change overlay apply input data from unflattened to FDT") Signed-off-by: Geert Uytterhoeven --- .../devicetree/configfs-overlays.txt | 31 ++ drivers/of/Kconfig | 8 + drivers/of/Makefile | 1 + drivers/of/configfs.c | 284 ++++++++++++++++++ 4 files changed, 324 insertions(+) create mode 100644 Documentation/devicetree/configfs-overlays.txt create mode 100644 drivers/of/configfs.c diff --git a/Documentation/devicetree/configfs-overlays.txt b/Documentation/devicetree/configfs-overlays.txt new file mode 100644 index 0000000000000..185d85ef52e49 --- /dev/null +++ b/Documentation/devicetree/configfs-overlays.txt @@ -0,0 +1,31 @@ +Howto use the configfs overlay interface. + +A device-tree configfs entry is created in /config/device-tree/overlays +and and it is manipulated using standard file system I/O. +Note that this is a debug level interface, for use by developers and +not necessarily something accessed by normal users due to the +security implications of having direct access to the kernel's device tree. + +* To create an overlay you mkdir the directory: + + # mkdir /config/device-tree/overlays/foo + +* Either you echo the overlay firmware file to the path property file. + + # echo foo.dtbo >/config/device-tree/overlays/foo/path + +* Or you cat the contents of the overlay to the dtbo file + + # cat foo.dtbo >/config/device-tree/overlays/foo/dtbo + +The overlay file will be applied, and devices will be created/destroyed +as required. + +To remove it simply rmdir the directory. + + # rmdir /config/device-tree/overlays/foo + +The rationale for the dual interface (firmware & direct copy) is that each is +better suited to different use patterns. The firmware interface is what's +intended to be used by hardware managers in the kernel, while the copy interface +make sense for developers (since it avoids problems with namespaces). diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index 18450437d5d5a..303737ebce435 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -93,6 +93,14 @@ config OF_OVERLAY While this option is selected automatically when needed, you can enable it manually to improve device tree unit test coverage. +config OF_CONFIGFS + bool "Device Tree Overlay ConfigFS interface" + select CONFIGFS_FS + select OF_FLATTREE + depends on OF_OVERLAY + help + Enable a simple user-space driven DT overlay interface. + config OF_NUMA bool diff --git a/drivers/of/Makefile b/drivers/of/Makefile index 6e1e5212f0589..3222259b03932 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-y = base.o device.o platform.o property.o obj-$(CONFIG_OF_KOBJ) += kobj.o +obj-$(CONFIG_OF_CONFIGFS) += configfs.o obj-$(CONFIG_OF_DYNAMIC) += dynamic.o obj-$(CONFIG_OF_FLATTREE) += fdt.o obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o diff --git a/drivers/of/configfs.c b/drivers/of/configfs.c new file mode 100644 index 0000000000000..7a6cae074381c --- /dev/null +++ b/drivers/of/configfs.c @@ -0,0 +1,284 @@ +/* + * Configfs entries for device-tree + * + * Copyright (C) 2013 - Pantelis Antoniou + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "of_private.h" + +struct cfs_overlay_item { + struct config_item item; + + char path[PATH_MAX]; + + const struct firmware *fw; + struct device_node *overlay; + int ov_id; + + void *dtbo; + int dtbo_size; +}; + +static int create_overlay(struct cfs_overlay_item *overlay, const void *blob, + size_t size) +{ + int err; + + err = of_overlay_fdt_apply(blob, size, &overlay->ov_id); + if (err < 0) + pr_err("%s: Failed to create overlay (err=%d)\n", __func__, + err); + + return err; +} + +static inline struct cfs_overlay_item *to_cfs_overlay_item( + struct config_item *item) +{ + return item ? container_of(item, struct cfs_overlay_item, item) : NULL; +} + +static ssize_t cfs_overlay_item_path_show(struct config_item *item, char *page) +{ + return sprintf(page, "%s\n", to_cfs_overlay_item(item)->path); +} + +static ssize_t cfs_overlay_item_path_store(struct config_item *item, + const char *page, size_t count) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + const char *p = page; + char *s; + int err; + + /* if it's set do not allow changes */ + if (overlay->path[0] != '\0' || overlay->dtbo_size > 0) + return -EPERM; + + /* copy to path buffer (and make sure it's always zero terminated */ + count = snprintf(overlay->path, sizeof(overlay->path) - 1, "%s", p); + overlay->path[sizeof(overlay->path) - 1] = '\0'; + + /* strip trailing newlines */ + s = overlay->path + strlen(overlay->path); + while (s > overlay->path && *--s == '\n') + *s = '\0'; + + pr_debug("%s: path is '%s'\n", __func__, overlay->path); + + err = request_firmware(&overlay->fw, overlay->path, NULL); + if (err != 0) + goto out_err; + + err = create_overlay(overlay, overlay->fw->data, overlay->fw->size); + if (err < 0) + goto out_err; + + return count; + +out_err: + + release_firmware(overlay->fw); + overlay->fw = NULL; + + overlay->path[0] = '\0'; + return err; +} + +static ssize_t cfs_overlay_item_status_show(struct config_item *item, + char *page) +{ + return sprintf(page, "%s\n", to_cfs_overlay_item(item)->ov_id >= 0 ? + "applied" : "unapplied"); +} + +CONFIGFS_ATTR(cfs_overlay_item_, path); +CONFIGFS_ATTR_RO(cfs_overlay_item_, status); + +static struct configfs_attribute *cfs_overlay_attrs[] = { + &cfs_overlay_item_attr_path, + &cfs_overlay_item_attr_status, + NULL, +}; + +static ssize_t cfs_overlay_item_dtbo_read(struct config_item *item, void *buf, + size_t max_count) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + + pr_debug("%s: buf=%p max_count=%zu\n", __func__, + buf, max_count); + + if (overlay->dtbo == NULL) + return 0; + + /* copy if buffer provided */ + if (buf != NULL) { + /* the buffer must be large enough */ + if (overlay->dtbo_size > max_count) + return -ENOSPC; + + memcpy(buf, overlay->dtbo, overlay->dtbo_size); + } + + return overlay->dtbo_size; +} + +static ssize_t cfs_overlay_item_dtbo_write(struct config_item *item, + const void *buf, size_t count) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + int err; + + /* if it's set do not allow changes */ + if (overlay->path[0] != '\0' || overlay->dtbo_size > 0) + return -EPERM; + + /* copy the contents */ + overlay->dtbo = kmemdup(buf, count, GFP_KERNEL); + if (overlay->dtbo == NULL) + return -ENOMEM; + + overlay->dtbo_size = count; + + err = create_overlay(overlay, overlay->dtbo, overlay->dtbo_size); + if (err < 0) + goto out_err; + + return count; + +out_err: + kfree(overlay->dtbo); + overlay->dtbo = NULL; + overlay->dtbo_size = 0; + + return err; +} + +CONFIGFS_BIN_ATTR(cfs_overlay_item_, dtbo, NULL, SZ_1M); + +static struct configfs_bin_attribute *cfs_overlay_bin_attrs[] = { + &cfs_overlay_item_attr_dtbo, + NULL, +}; + +static void cfs_overlay_release(struct config_item *item) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + + if (overlay->ov_id >= 0) + of_overlay_remove(&overlay->ov_id); + if (overlay->fw) + release_firmware(overlay->fw); + /* kfree with NULL is safe */ + kfree(overlay->dtbo); + kfree(overlay); +} + +static struct configfs_item_operations cfs_overlay_item_ops = { + .release = cfs_overlay_release, +}; + +static struct config_item_type cfs_overlay_type = { + .ct_item_ops = &cfs_overlay_item_ops, + .ct_attrs = cfs_overlay_attrs, + .ct_bin_attrs = cfs_overlay_bin_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_item *cfs_overlay_group_make_item( + struct config_group *group, const char *name) +{ + struct cfs_overlay_item *overlay; + + overlay = kzalloc(sizeof(*overlay), GFP_KERNEL); + if (!overlay) + return ERR_PTR(-ENOMEM); + overlay->ov_id = -1; + + config_item_init_type_name(&overlay->item, name, &cfs_overlay_type); + return &overlay->item; +} + +static void cfs_overlay_group_drop_item(struct config_group *group, + struct config_item *item) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + + config_item_put(&overlay->item); +} + +static struct configfs_group_operations overlays_ops = { + .make_item = cfs_overlay_group_make_item, + .drop_item = cfs_overlay_group_drop_item, +}; + +static struct config_item_type overlays_type = { + .ct_group_ops = &overlays_ops, + .ct_owner = THIS_MODULE, +}; + +static struct configfs_group_operations of_cfs_ops = { + /* empty - we don't allow anything to be created */ +}; + +static struct config_item_type of_cfs_type = { + .ct_group_ops = &of_cfs_ops, + .ct_owner = THIS_MODULE, +}; + +static struct config_group of_cfs_overlay_group; + +static struct configfs_subsystem of_cfs_subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = "device-tree", + .ci_type = &of_cfs_type, + }, + }, + .su_mutex = __MUTEX_INITIALIZER(of_cfs_subsys.su_mutex), +}; + +static int __init of_cfs_init(void) +{ + int ret; + + pr_info("%s\n", __func__); + + config_group_init(&of_cfs_subsys.su_group); + config_group_init_type_name(&of_cfs_overlay_group, "overlays", + &overlays_type); + configfs_add_default_group(&of_cfs_overlay_group, + &of_cfs_subsys.su_group); + + ret = configfs_register_subsystem(&of_cfs_subsys); + if (ret != 0) { + pr_err("%s: failed to register subsys\n", __func__); + goto out; + } + pr_info("%s: OK\n", __func__); +out: + return ret; +} +late_initcall(of_cfs_init); From 49281bd5dd68b4de3f1dc0190fc1876baeb18bdd Mon Sep 17 00:00:00 2001 From: Pantelis Antoniou Date: Sun, 15 Mar 2015 20:39:36 +0200 Subject: [PATCH 27/65] of: overlay: kobjectify overlay objects We are going to need the overlays to appear on sysfs with runtime global properties (like master enable) so turn them into kobjects. They have to be in sysfs so that people can have information about the overlays applied in the system, i.e. where their targets are and whether removal is possible. In a future more attributes can be added in a backwards compatible manner. Signed-off-by: Pantelis Antoniou [geert: Rebase to v4.15-rc1] [Fengguang Wu: Make overlay_changeset_release() static] [geert: Rebase on top of commit 39a751a4cb7e4798 ("of: change overlay apply input data from unflattened to FDT") Signed-off-by: Geert Uytterhoeven --- drivers/of/base.c | 5 +++++ drivers/of/of_private.h | 9 ++++++++ drivers/of/overlay.c | 48 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+) diff --git a/drivers/of/base.c b/drivers/of/base.c index a44a0e7ba2510..182a1b9c6ab00 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -166,6 +166,7 @@ void __of_phandle_cache_inv_entry(phandle handle) void __init of_core_init(void) { struct device_node *np; + int ret; /* Create the kset, and register existing nodes */ @@ -186,6 +187,10 @@ void __init of_core_init(void) /* Symlink in /proc as required by userspace ABI */ if (of_root) proc_symlink("device-tree", NULL, "/sys/firmware/devicetree/base"); + + ret = of_overlay_init(); + if (ret != 0) + pr_warn("of_init: of_overlay_init failed!\n"); } static struct property *__of_find_property(const struct device_node *np, diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index d9e6a324de0a7..85c2e1455bd14 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -169,4 +169,13 @@ static inline int of_dma_get_range(struct device_node *np, } #endif +#if defined(CONFIG_OF_OVERLAY) +extern int of_overlay_init(void); +#else +static inline int of_overlay_init(void) +{ + return 0; +} +#endif + #endif /* _LINUX_OF_PRIVATE_H */ diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index c8a0c0e9dec1c..b9e3a6703c348 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "of_private.h" @@ -73,6 +74,7 @@ struct overlay_changeset { struct fragment *fragments; bool symbols_fragment; struct of_changeset cset; + struct kobject kobj; }; /* flags are sticky - once set, do not reset */ @@ -858,6 +860,18 @@ static void free_overlay_changeset(struct overlay_changeset *ovcs) of_node_put(ovcs->fragments[i].overlay); } kfree(ovcs->fragments); + kobject_put(&ovcs->kobj); +} + +static inline struct overlay_changeset *kobj_to_ovcs(struct kobject *kobj) +{ + return container_of(kobj, struct overlay_changeset, kobj); +} + +static void overlay_changeset_release(struct kobject *kobj) +{ + struct overlay_changeset *ovcs = kobj_to_ovcs(kobj); + /* * There should be no live pointers into ovcs->overlay_tree and * ovcs->fdt due to the policy that overlay notifiers are not allowed @@ -868,6 +882,12 @@ static void free_overlay_changeset(struct overlay_changeset *ovcs) kfree(ovcs); } +static struct kobj_type overlay_changeset_ktype = { + .release = overlay_changeset_release, +}; + +static struct kset *ov_kset; + /* * internal documentation * @@ -938,6 +958,8 @@ static int of_overlay_apply(const void *fdt, struct device_node *tree, goto out; } + kobject_init(&ovcs->kobj, &overlay_changeset_ktype); + of_overlay_mutex_lock(); mutex_lock(&of_mutex); @@ -975,6 +997,22 @@ static int of_overlay_apply(const void *fdt, struct device_node *tree, goto err_free_overlay_changeset; } + ovcs->kobj.kset = ov_kset; + ret = kobject_add(&ovcs->kobj, NULL, "%d", ovcs->id); + if (ret != 0) { + pr_err("%s: kobject_add() failed for tree@%s\n", __func__, + tree->full_name); + ret_tmp = 0; + ret_revert = __of_changeset_revert_entries(&ovcs->cset, + &ret_tmp); + if (ret_revert) { + pr_debug("overlay changeset revert error %d\n", + ret_revert); + devicetree_state_flags |= DTSF_REVERT_FAIL; + } + goto err_free_overlay_changeset; + } + ret = __of_changeset_apply_notify(&ovcs->cset); if (ret) pr_err("overlay apply changeset entry notify error %d\n", ret); @@ -1274,3 +1312,13 @@ int of_overlay_remove_all(void) return 0; } EXPORT_SYMBOL_GPL(of_overlay_remove_all); + +/* called from of_init() */ +int of_overlay_init(void) +{ + ov_kset = kset_create_and_add("overlays", NULL, &of_kset->kobj); + if (!ov_kset) + return -ENOMEM; + + return 0; +} From e752e188c363f6abd53f2ac1e2f1eb22d2086371 Mon Sep 17 00:00:00 2001 From: Pantelis Antoniou Date: Tue, 17 Mar 2015 15:25:46 +0200 Subject: [PATCH 28/65] of: overlay: global sysfs enable attribute A throw once master enable switch to protect against any further overlay applications if the administrator desires so. A kernel command line option is provided as well. Signed-off-by: Pantelis Antoniou --- drivers/of/overlay.c | 51 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index b9e3a6703c348..a94ecce9f8998 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "of_private.h" @@ -77,6 +78,16 @@ struct overlay_changeset { struct kobject kobj; }; +/* master enable switch; once set to 0 can't be re-enabled */ +static atomic_t ov_enable = ATOMIC_INIT(1); + +static int __init of_overlay_disable_setup(char *str __always_unused) +{ + atomic_set(&ov_enable, 0); + return 1; +} +__setup("of_overlay_disable", of_overlay_disable_setup); + /* flags are sticky - once set, do not reset */ static int devicetree_state_flags; #define DTSF_APPLY_FAIL 0x01 @@ -882,6 +893,35 @@ static void overlay_changeset_release(struct kobject *kobj) kfree(ovcs); } +static ssize_t enable_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&ov_enable)); +} + +static ssize_t enable_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int ret; + bool new_enable; + + ret = strtobool(buf, &new_enable); + if (ret != 0) + return ret; + /* if we've disabled it, no going back */ + if (atomic_read(&ov_enable) == 0) + return -EPERM; + atomic_set(&ov_enable, (int)new_enable); + return count; +} + +static struct kobj_attribute enable_attr = __ATTR_RW(enable); + +static const struct attribute *overlay_global_attrs[] = { + &enable_attr.attr, + NULL +}; + static struct kobj_type overlay_changeset_ktype = { .release = overlay_changeset_release, }; @@ -942,6 +982,10 @@ static int of_overlay_apply(const void *fdt, struct device_node *tree, * overlay changeset code is responsible for freeing them. */ + /* administratively disabled */ + if (!atomic_read(&ov_enable)) + return -EPERM; + if (devicetree_corrupt()) { pr_err("devicetree state suspect, refuse to apply overlay\n"); kfree(fdt); @@ -1316,9 +1360,14 @@ EXPORT_SYMBOL_GPL(of_overlay_remove_all); /* called from of_init() */ int of_overlay_init(void) { + int rc; + ov_kset = kset_create_and_add("overlays", NULL, &of_kset->kobj); if (!ov_kset) return -ENOMEM; - return 0; + rc = sysfs_create_files(&ov_kset->kobj, overlay_global_attrs); + WARN(rc, "%s: error adding global attributes\n", __func__); + + return rc; } From adfef06a239042a3b2f02e1db55bff841c746c53 Mon Sep 17 00:00:00 2001 From: Pantelis Antoniou Date: Tue, 17 Mar 2015 21:42:10 +0200 Subject: [PATCH 29/65] Documentation: ABI: overlays - global attributes Documentation ABI entry for overlays sysfs entries. Signed-off-by: Pantelis Antoniou --- .../sysfs-firmware-devicetree-overlays | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-firmware-devicetree-overlays diff --git a/Documentation/ABI/testing/sysfs-firmware-devicetree-overlays b/Documentation/ABI/testing/sysfs-firmware-devicetree-overlays new file mode 100644 index 0000000000000..e938f440c63dc --- /dev/null +++ b/Documentation/ABI/testing/sysfs-firmware-devicetree-overlays @@ -0,0 +1,24 @@ +What: /sys/firmware/devicetree/overlays/ +Date: October 2015 +Contact: Pantelis Antoniou +Description: + This directory contains the applied device tree overlays of + the running system, as directories of the overlay id. + +What: /sys/firmware/devicetree/overlays/enable +Date: October 2015 +Contact: Pantelis Antoniou +Description: + The master enable switch, by default is 1, and when + set to 0 it cannot be re-enabled for security reasons. + + The discussion about this switch takes place in: + http://comments.gmane.org/gmane.linux.drivers.devicetree/101871 + + Kees Cook: + "Coming from the perspective of drawing a bright line between + kernel and the root user (which tends to start with disabling + kernel module loading), I would say that there at least needs + to be a high-level one-way "off" switch for the interface so + that systems that have this interface can choose to turn it off + during initial boot, etc." From 2d6c4ed22abf0b581c6e11dd3fba72be283e2689 Mon Sep 17 00:00:00 2001 From: Pantelis Antoniou Date: Thu, 22 Oct 2015 22:37:06 +0300 Subject: [PATCH 30/65] Documentation: document of_overlay_disable parameter Document the of_overlay_disable parameter. Signed-off-by: Pantelis Antoniou --- Documentation/admin-guide/kernel-parameters.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index f577c29f20930..22129925f6133 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -3485,6 +3485,8 @@ This can be set from sysctl after boot. See Documentation/admin-guide/sysctl/vm.rst for details. + of_overlay_disable [OF] Disable device tree overlays at boot time. + ohci1394_dma=early [HW] enable debugging via the ohci1394 driver. See Documentation/core-api/debugging-via-ohci1394.rst for more info. From ca3ad6cbd3c623b2894ecd350aefc531a5954496 Mon Sep 17 00:00:00 2001 From: Pantelis Antoniou Date: Thu, 23 Apr 2015 19:02:16 +0300 Subject: [PATCH 31/65] of: overlay: add per overlay sysfs attributes * A per overlay can_remove sysfs attribute that reports whether the overlay can be removed or not due to another overlapping overlay. * A target sysfs attribute listing the target of each fragment, in a group named after the name of the fragment. Signed-off-by: Pantelis Antoniou [geert: Setup ovinfo[cnt].info for symbols] [geert: Spelling s/changset/changeset/] [geert: Rebase on top of commit 39a751a4cb7e4798 ("of: change overlay apply input data from unflattened to FDT") [geert: Use "%pOF" instead of of_node_full_name()] Signed-off-by: Geert Uytterhoeven --- drivers/of/overlay.c | 111 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 101 insertions(+), 10 deletions(-) diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index a94ecce9f8998..3da2ebe211e02 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -25,6 +25,20 @@ #include "of_private.h" +/* fwd. decl */ +struct overlay_changeset; +struct fragment; + +/* an attribute for each fragment */ +struct fragment_attribute { + struct attribute attr; + ssize_t (*show)(struct kobject *kobj, struct fragment_attribute *fattr, + char *buf); + ssize_t (*store)(struct kobject *kobj, struct fragment_attribute *fattr, + const char *buf, size_t count); + struct fragment *fragment; +}; + /** * struct target - info about current target node as recursing through overlay * @np: node where current level of overlay will be applied @@ -47,12 +61,18 @@ struct target { /** * struct fragment - info about fragment nodes in overlay expanded device tree + * @info: info node that contains the target and overlay * @target: target of the overlay operation * @overlay: pointer to the __overlay__ node */ struct fragment { struct device_node *overlay; struct device_node *target; + struct overlay_changeset *ovcs; + struct device_node *info; + struct attribute_group attr_group; + struct attribute *attrs[2]; + struct fragment_attribute target_attr; }; /** @@ -73,6 +93,7 @@ struct overlay_changeset { struct device_node *overlay_tree; int count; struct fragment *fragments; + const struct attribute_group **attr_groups; bool symbols_fragment; struct of_changeset cset; struct kobject kobj; @@ -106,6 +127,7 @@ static int devicetree_corrupt(void) static int build_changeset_next_level(struct overlay_changeset *ovcs, struct target *target, const struct device_node *overlay_node); +static int overlay_removal_is_ok(struct overlay_changeset *ovcs); /* * of_resolve_phandles() finds the largest phandle in the live tree. @@ -727,6 +749,16 @@ static struct device_node *find_target(struct device_node *info_node) return NULL; } +static ssize_t target_show(struct kobject *kobj, + struct fragment_attribute *fattr, char *buf) +{ + struct fragment *fragment = fattr->fragment; + + return snprintf(buf, PAGE_SIZE, "%pOF\n", fragment->target); +} + +static const struct fragment_attribute target_template_attr = __ATTR_RO(target); + /** * init_overlay_changeset() - initialize overlay changeset from overlay tree * @ovcs: Overlay changeset to build @@ -746,7 +778,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs, struct device_node *node, *overlay_node; struct fragment *fragment; struct fragment *fragments; - int cnt, id, ret; + int i, cnt, id, ret; /* * Warn for some issues. Can not return -EINVAL for these until @@ -811,6 +843,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs, goto err_free_fragments; } + fragment->info = of_node_get(node); cnt++; } @@ -831,6 +864,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs, goto err_free_fragments; } + fragment->info = of_node_get(node); cnt++; } @@ -844,6 +878,34 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs, ovcs->count = cnt; ovcs->fragments = fragments; + ovcs->attr_groups = kcalloc(cnt + 1, sizeof(struct attribute_group *), + GFP_KERNEL); + if (ovcs->attr_groups == NULL) { + ret = -ENOMEM; + goto err_free_fragments; + } + + for (i = 0; i < cnt; i++) { + fragment = &ovcs->fragments[i]; + + ovcs->attr_groups[i] = &fragment->attr_group; + + fragment->target_attr = target_template_attr; + /* make lockdep happy */ + sysfs_attr_init(&fragment->target_attr.attr); + fragment->target_attr.fragment = fragment; + + fragment->attrs[0] = &fragment->target_attr.attr; + fragment->attrs[1] = NULL; + + /* NOTE: direct reference to the full_name */ + fragment->attr_group.name = + kbasename(fragment->info->full_name); + fragment->attr_group.attrs = fragment->attrs; + + } + ovcs->attr_groups[i] = NULL; + return 0; err_free_fragments: @@ -866,9 +928,12 @@ static void free_overlay_changeset(struct overlay_changeset *ovcs) if (ovcs->id) idr_remove(&ovcs_idr, ovcs->id); + kfree(ovcs->attr_groups); + for (i = 0; i < ovcs->count; i++) { of_node_put(ovcs->fragments[i].target); of_node_put(ovcs->fragments[i].overlay); + of_node_put(ovcs->fragments[i].info); } kfree(ovcs->fragments); kobject_put(&ovcs->kobj); @@ -922,8 +987,25 @@ static const struct attribute *overlay_global_attrs[] = { NULL }; +static ssize_t can_remove_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct overlay_changeset *ovcs = kobj_to_ovcs(kobj); + + return snprintf(buf, PAGE_SIZE, "%d\n", overlay_removal_is_ok(ovcs)); +} + +static struct kobj_attribute can_remove_attr = __ATTR_RO(can_remove); + +static struct attribute *overlay_changeset_attrs[] = { + &can_remove_attr.attr, + NULL +}; + static struct kobj_type overlay_changeset_ktype = { .release = overlay_changeset_release, + .sysfs_ops = &kobj_sysfs_ops, /* default kobj sysfs ops */ + .default_attrs = overlay_changeset_attrs, }; static struct kset *ov_kset; @@ -1046,15 +1128,14 @@ static int of_overlay_apply(const void *fdt, struct device_node *tree, if (ret != 0) { pr_err("%s: kobject_add() failed for tree@%s\n", __func__, tree->full_name); - ret_tmp = 0; - ret_revert = __of_changeset_revert_entries(&ovcs->cset, - &ret_tmp); - if (ret_revert) { - pr_debug("overlay changeset revert error %d\n", - ret_revert); - devicetree_state_flags |= DTSF_REVERT_FAIL; - } - goto err_free_overlay_changeset; + goto err_revert; + } + + ret = sysfs_create_groups(&ovcs->kobj, ovcs->attr_groups); + if (ret != 0) { + pr_err("%s: sysfs_create_groups() failed for tree@%s\n", + __func__, tree->full_name); + goto err_revert; } ret = __of_changeset_apply_notify(&ovcs->cset); @@ -1075,6 +1156,14 @@ static int of_overlay_apply(const void *fdt, struct device_node *tree, goto out_unlock; +err_revert: + ret_tmp = 0; + ret_revert = __of_changeset_revert_entries(&ovcs->cset, &ret_tmp); + if (ret_revert) { + pr_debug("overlay changeset revert error %d\n", ret_revert); + devicetree_state_flags |= DTSF_REVERT_FAIL; + } + err_free_tree: kfree(fdt); kfree(tree); @@ -1299,6 +1388,8 @@ int of_overlay_remove(int *ovcs_id) list_del(&ovcs->ovcs_list); + sysfs_remove_groups(&ovcs->kobj, ovcs->attr_groups); + ret_apply = 0; ret = __of_changeset_revert_entries(&ovcs->cset, &ret_apply); if (ret) { From 71962f3559e54ec65e2f7d7d2782bf45c25a3a40 Mon Sep 17 00:00:00 2001 From: Pantelis Antoniou Date: Thu, 22 Oct 2015 20:59:27 +0300 Subject: [PATCH 32/65] Documentation: ABI: overlays - per overlay docs Documentation for the per-overlay attributes. Signed-off-by: Pantelis Antoniou --- .../sysfs-firmware-devicetree-overlays | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-firmware-devicetree-overlays b/Documentation/ABI/testing/sysfs-firmware-devicetree-overlays index e938f440c63dc..88d15498a21be 100644 --- a/Documentation/ABI/testing/sysfs-firmware-devicetree-overlays +++ b/Documentation/ABI/testing/sysfs-firmware-devicetree-overlays @@ -22,3 +22,31 @@ Description: to be a high-level one-way "off" switch for the interface so that systems that have this interface can choose to turn it off during initial boot, etc." + +What: /sys/firmware/devicetree/overlays/ +Date: October 2015 +Contact: Pantelis Antoniou +Description: + Each directory represents an applied overlay, containing + the following attribute files. + +What: /sys/firmware/devicetree/overlays//can_remove +Date: October 2015 +Contact: Pantelis Antoniou +Description: + The attribute set to 1 means that the overlay can be removed, + while 0 means that the overlay is being overlapped therefore + removal is prohibited. + +What: /sys/firmware/devicetree/overlays/// +Date: October 2015 +Contact: Pantelis Antoniou +Description: + Each of these directories contain information about of the + particular overlay fragment. + +What: /sys/firmware/devicetree/overlays///target +Date: October 2015 +Contact: Pantelis Antoniou +Description: + The full-path of the target of the fragment From 1ed4826863ed9aef53379a433dc069d9b9f87eed Mon Sep 17 00:00:00 2001 From: Pantelis Antoniou Date: Wed, 14 Oct 2015 13:20:54 +0300 Subject: [PATCH 33/65] of: rename *_node_sysfs to _node_post Signed-off-by: Pantelis Antoniou [geert: Convert new user in of_unittest_overlay_high_level()] [geert: Rebase to v4.15-rc1] [geert: Rebase on top of commit 39a751a4cb7e4798 ("of: change overlay apply input data from unflattened to FDT") Signed-off-by: Geert Uytterhoeven --- drivers/of/base.c | 2 +- drivers/of/dynamic.c | 8 ++++---- drivers/of/kobj.c | 4 ++-- drivers/of/of_private.h | 12 ++++++------ drivers/of/unittest.c | 6 +++--- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/of/base.c b/drivers/of/base.c index 182a1b9c6ab00..91eea0ec5e7df 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -178,7 +178,7 @@ void __init of_core_init(void) return; } for_each_of_allnodes(np) { - __of_attach_node_sysfs(np); + __of_attach_node_post(np); if (np->phandle && !phandle_cache[of_phandle_cache_hash(np->phandle)]) phandle_cache[of_phandle_cache_hash(np->phandle)] = np; } diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index fe64430b438a1..7478bfc8d440f 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -243,7 +243,7 @@ int of_attach_node(struct device_node *np) __of_attach_node(np); raw_spin_unlock_irqrestore(&devtree_lock, flags); - __of_attach_node_sysfs(np); + __of_attach_node_post(np); mutex_unlock(&of_mutex); of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, &rd); @@ -295,7 +295,7 @@ int of_detach_node(struct device_node *np) __of_detach_node(np); raw_spin_unlock_irqrestore(&devtree_lock, flags); - __of_detach_node_sysfs(np); + __of_detach_node_post(np); mutex_unlock(&of_mutex); of_reconfig_notify(OF_RECONFIG_DETACH_NODE, &rd); @@ -634,10 +634,10 @@ static int __of_changeset_entry_apply(struct of_changeset_entry *ce) switch (ce->action) { case OF_RECONFIG_ATTACH_NODE: - __of_attach_node_sysfs(ce->np); + __of_attach_node_post(ce->np); break; case OF_RECONFIG_DETACH_NODE: - __of_detach_node_sysfs(ce->np); + __of_detach_node_post(ce->np); break; case OF_RECONFIG_ADD_PROPERTY: /* ignore duplicate names */ diff --git a/drivers/of/kobj.c b/drivers/of/kobj.c index 6675b5e56960c..0443582c83219 100644 --- a/drivers/of/kobj.c +++ b/drivers/of/kobj.c @@ -112,7 +112,7 @@ void __of_update_property_sysfs(struct device_node *np, struct property *newprop __of_add_property_sysfs(np, newprop); } -int __of_attach_node_sysfs(struct device_node *np) +int __of_attach_node_post(struct device_node *np) { const char *name; struct kobject *parent; @@ -146,7 +146,7 @@ int __of_attach_node_sysfs(struct device_node *np) return 0; } -void __of_detach_node_sysfs(struct device_node *np) +void __of_detach_node_post(struct device_node *np) { struct property *pp; diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index 85c2e1455bd14..8f1234538c675 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -63,8 +63,8 @@ int __of_add_property_sysfs(struct device_node *np, struct property *pp); void __of_remove_property_sysfs(struct device_node *np, struct property *prop); void __of_update_property_sysfs(struct device_node *np, struct property *newprop, struct property *oldprop); -int __of_attach_node_sysfs(struct device_node *np); -void __of_detach_node_sysfs(struct device_node *np); +int __of_attach_node_post(struct device_node *np); +void __of_detach_node_post(struct device_node *np); #else static inline int __of_add_property_sysfs(struct device_node *np, struct property *pp) { @@ -73,11 +73,11 @@ static inline int __of_add_property_sysfs(struct device_node *np, struct propert static inline void __of_remove_property_sysfs(struct device_node *np, struct property *prop) {} static inline void __of_update_property_sysfs(struct device_node *np, struct property *newprop, struct property *oldprop) {} -static inline int __of_attach_node_sysfs(struct device_node *np) +static inline int __of_attach_node_post(struct device_node *np) { return 0; } -static inline void __of_detach_node_sysfs(struct device_node *np) {} +static inline void __of_detach_node_post(struct device_node *np) {} #endif #if defined(CONFIG_OF_RESOLVE) @@ -135,9 +135,9 @@ extern int __of_update_property(struct device_node *np, extern void __of_update_property_sysfs(struct device_node *np, struct property *newprop, struct property *oldprop); -extern int __of_attach_node_sysfs(struct device_node *np); +extern int __of_attach_node_post(struct device_node *np); extern void __of_detach_node(struct device_node *np); -extern void __of_detach_node_sysfs(struct device_node *np); +extern void __of_detach_node_post(struct device_node *np); extern void __of_sysfs_remove_bin_file(struct device_node *np, struct property *prop); diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index 5407bbdb64395..f3fbdf625aef3 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -1400,7 +1400,7 @@ static void attach_node_and_children(struct device_node *np) of_node_clear_flag(np, OF_DETACHED); raw_spin_unlock_irqrestore(&devtree_lock, flags); - __of_attach_node_sysfs(np); + __of_attach_node_post(np); mutex_unlock(&of_mutex); while (child) { @@ -1460,7 +1460,7 @@ static int __init unittest_data_add(void) if (!of_root) { of_root = unittest_data_node; for_each_of_allnodes(np) - __of_attach_node_sysfs(np); + __of_attach_node_post(np); of_aliases = of_find_node_by_path("/aliases"); of_chosen = of_find_node_by_path("/chosen"); of_overlay_mutex_unlock(); @@ -3124,7 +3124,7 @@ static __init void of_unittest_overlay_high_level(void) of_root->child = overlay_base_root->child; for_each_of_allnodes_from(overlay_base_root, np) - __of_attach_node_sysfs(np); + __of_attach_node_post(np); if (of_symbols) { struct property *new_prop; From 99235a3ec81bf6d700c19f47926cb647c77b9f0e Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Fri, 5 Apr 2019 09:51:32 -0500 Subject: [PATCH 34/65] ar1021_i2c: invert/swap and offset options Signed-off-by: Robert Nelson --- drivers/input/touchscreen/ar1021_i2c.c | 81 ++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 5 deletions(-) diff --git a/drivers/input/touchscreen/ar1021_i2c.c b/drivers/input/touchscreen/ar1021_i2c.c index c0d5c24133563..740956f9431a7 100644 --- a/drivers/input/touchscreen/ar1021_i2c.c +++ b/drivers/input/touchscreen/ar1021_i2c.c @@ -17,6 +17,7 @@ #define AR1021_MAX_X 4095 #define AR1021_MAX_Y 4095 +#define AR1021_MAX_PRESSURE 255 #define AR1021_CMD 0x55 @@ -26,8 +27,29 @@ struct ar1021_i2c { struct i2c_client *client; struct input_dev *input; u8 data[AR1021_TOUCH_PKG_SIZE]; + bool invert_x; + bool invert_y; + bool swap_xy; }; +static bool ar1021_get_prop_u32(struct device *dev, + const char *property, + unsigned int default_value, + unsigned int *value) +{ + u32 val; + int error; + + error = device_property_read_u32(dev, property, &val); + if (error) { + *value = default_value; + return false; + } + + *value = val; + return true; +} + static irqreturn_t ar1021_i2c_irq(int irq, void *dev_id) { struct ar1021_i2c *ar1021 = dev_id; @@ -49,9 +71,22 @@ static irqreturn_t ar1021_i2c_irq(int irq, void *dev_id) x = ((data[2] & 0x1f) << 7) | (data[1] & 0x7f); y = ((data[4] & 0x1f) << 7) | (data[3] & 0x7f); - input_report_abs(input, ABS_X, x); - input_report_abs(input, ABS_Y, y); + if (ar1021->invert_x) + x = AR1021_MAX_X - x; + + if (ar1021->invert_y) + y = AR1021_MAX_Y - y; + + if (ar1021->swap_xy) { + input_report_abs(input, ABS_X, y); + input_report_abs(input, ABS_Y, x); + } else { + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + } + input_report_key(input, BTN_TOUCH, button); + input_report_abs(input, ABS_PRESSURE, AR1021_MAX_PRESSURE); input_sync(input); out: @@ -93,6 +128,8 @@ static int ar1021_i2c_probe(struct i2c_client *client, struct ar1021_i2c *ar1021; struct input_dev *input; int error; + unsigned int offset_x, offset_y; + bool data_present; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { dev_err(&client->dev, "i2c_check_functionality error\n"); @@ -116,10 +153,44 @@ static int ar1021_i2c_probe(struct i2c_client *client, input->open = ar1021_i2c_open; input->close = ar1021_i2c_close; + ar1021->invert_x = device_property_read_bool(&client->dev, "touchscreen-inverted-x"); + ar1021->invert_y = device_property_read_bool(&client->dev, "touchscreen-inverted-y"); + ar1021->swap_xy = device_property_read_bool(&client->dev, "touchscreen-swapped-x-y"); + + data_present = ar1021_get_prop_u32(&client->dev, + "touchscreen-offset-x", + 0, + &offset_x); + + if (data_present) + dev_info(&client->dev, "touchscreen-offset-x: %d\n", offset_x); + + data_present = ar1021_get_prop_u32(&client->dev, + "touchscreen-offset-y", + 0, + &offset_y); + + if (data_present) + dev_info(&client->dev, "touchscreen-offset-y: %d\n", offset_y); + __set_bit(INPUT_PROP_DIRECT, input->propbit); - input_set_capability(input, EV_KEY, BTN_TOUCH); - input_set_abs_params(input, ABS_X, 0, AR1021_MAX_X, 0, 0); - input_set_abs_params(input, ABS_Y, 0, AR1021_MAX_Y, 0, 0); + //input_set_capability(input, EV_KEY, BTN_TOUCH); + + input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + if(ar1021->swap_xy) + { + input_set_abs_params(input, ABS_X, 0, AR1021_MAX_Y, 0, 0); + input_set_abs_params(input, ABS_Y, 0, AR1021_MAX_X, 0, 0); + } + else + { + input_set_abs_params(input, ABS_X, offset_x, AR1021_MAX_X-offset_x, 0, 0); + input_set_abs_params(input, ABS_Y, offset_y, AR1021_MAX_Y-offset_y, 0, 0); + } + + input_set_abs_params(input, ABS_PRESSURE, 0, AR1021_MAX_PRESSURE, 0, 0); input_set_drvdata(input, ar1021); From 47033cccf244a0c5ccbbee6ddffce53964a30458 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Fri, 8 May 2020 10:46:39 -0500 Subject: [PATCH 35/65] sound: enable SND_SOC_PCM5102A Signed-off-by: Robert Nelson --- sound/soc/codecs/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 25f331551f689..5f0a6ef1044cb 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1007,7 +1007,7 @@ config SND_SOC_PCM3168A_SPI select REGMAP_SPI config SND_SOC_PCM5102A - tristate + tristate "Texas Instruments PCM5102A CODEC" config SND_SOC_PCM512x tristate From b04411f95a3ff830cb956d786e1b459e71579e10 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Sun, 28 Oct 2018 16:14:23 -0500 Subject: [PATCH 36/65] NFM: spi: spidev: allow use of spidev in DT This reverts commit 956b200a846e324322f6211034c734c65a38e550. Signed-off-by: Robert Nelson --- drivers/spi/spidev.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index f56e0e975a469..2c72729d8280c 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -739,9 +739,9 @@ static int spidev_probe(struct spi_device *spi) * compatible string, it is a Linux implementation thing * rather than a description of the hardware. */ - WARN(spi->dev.of_node && - of_device_is_compatible(spi->dev.of_node, "spidev"), - "%pOF: buggy DT: spidev listed directly in DT\n", spi->dev.of_node); +// WARN(spi->dev.of_node && +// of_device_is_compatible(spi->dev.of_node, "spidev"), +// "%pOF: buggy DT: spidev listed directly in DT\n", spi->dev.of_node); spidev_probe_acpi(spi); From 33345438586ec6f99bf9addc19f4c06533c41341 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Fri, 28 Oct 2016 14:14:27 -0500 Subject: [PATCH 37/65] HACK: tps65217_pwr_but Signed-off-by: Robert Nelson --- drivers/input/misc/tps65218-pwrbutton.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/misc/tps65218-pwrbutton.c b/drivers/input/misc/tps65218-pwrbutton.c index f011447c44fb6..e0c6deacca181 100644 --- a/drivers/input/misc/tps65218-pwrbutton.c +++ b/drivers/input/misc/tps65218-pwrbutton.c @@ -36,7 +36,7 @@ struct tps6521x_data { static const struct tps6521x_data tps65217_data = { .reg_status = TPS65217_REG_STATUS, .pb_mask = TPS65217_STATUS_PB, - .name = "tps65217_pwrbutton", + .name = "tps65217_pwr_but", }; static const struct tps6521x_data tps65218_data = { From 9dde38c54c3092fe04d950f77d38a4ced8aec2de Mon Sep 17 00:00:00 2001 From: Jay at Control Module Industries Date: Tue, 19 May 2015 13:35:14 -0500 Subject: [PATCH 38/65] cpsw: search for phy I have encountered the same issue(s) on A6A boards. I couldn't find a patch, so I wrote this patch to update the device tree in the davinci_mdio driver in the 3.15.1 tree, it seems to correct it. I would welcome any input on a different approach. https://groups.google.com/d/msg/beagleboard/9mctrG26Mc8/SRlnumt0LoMJ v4.1-rcX: added hack around CONFIG_OF_OVERLAY v4.2-rc3+: added if (of_machine_is_compatible("ti,am335x-bone")) so we do not break dual ethernet am335x devices Signed-off-by: Robert Nelson --- drivers/net/ethernet/ti/davinci_mdio.c | 97 ++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/drivers/net/ethernet/ti/davinci_mdio.c b/drivers/net/ethernet/ti/davinci_mdio.c index 383e2ba16b500..a508ad41c630b 100644 --- a/drivers/net/ethernet/ti/davinci_mdio.c +++ b/drivers/net/ethernet/ti/davinci_mdio.c @@ -108,6 +108,10 @@ struct davinci_mdio_data { bool manual_mode; }; +#if IS_ENABLED(CONFIG_OF) +static void davinci_mdio_update_dt_from_phymask(u32 phy_mask); +#endif + static void davinci_mdio_init_clk(struct davinci_mdio_data *data) { u32 mdio_in, div, mdio_out_khz, access_time; @@ -297,6 +301,12 @@ static int davinci_mdio_common_reset(struct davinci_mdio_data *data) /* restrict mdio bus to live phys only */ dev_info(data->dev, "detected phy mask %x\n", ~phy_mask); phy_mask = ~phy_mask; + + #if IS_ENABLED(CONFIG_OF) + if (of_machine_is_compatible("ti,am335x-bone")) + davinci_mdio_update_dt_from_phymask(phy_mask); + #endif + } else { /* desperately scan all phys */ dev_warn(data->dev, "no live phy, scanning all\n"); @@ -694,6 +704,93 @@ static int davinci_mdio_runtime_resume(struct device *dev) } return 0; } +static void davinci_mdio_update_dt_from_phymask(u32 phy_mask) +{ + int i, len, skip; + u32 addr; + __be32 *old_phy_p, *phy_id_p; + struct property *phy_id_property = NULL; + struct device_node *node_p, *slave_p; + + addr = 0; + + for (i = 0; i < PHY_MAX_ADDR; i++) { + if ((phy_mask & (1 << i)) == 0) { + addr = (u32) i; + break; + } + } + + for_each_compatible_node(node_p, NULL, "ti,cpsw") { + for_each_node_by_name(slave_p, "slave") { + +#if IS_ENABLED(CONFIG_OF_OVERLAY) + skip = 1; + // Hack, the overlay fixup "slave" doesn't have phy-mode... + old_phy_p = (__be32 *) of_get_property(slave_p, "phy-mode", &len); + + if (len != (sizeof(__be32 *) * 1)) + { + skip = 0; + } + + if (skip) { +#endif + + old_phy_p = (__be32 *) of_get_property(slave_p, "phy_id", &len); + + if (len != (sizeof(__be32 *) * 2)) + goto err_out; + + if (old_phy_p) { + + phy_id_property = kzalloc(sizeof(*phy_id_property), GFP_KERNEL); + + if (! phy_id_property) + goto err_out; + + phy_id_property->length = len; + phy_id_property->name = kstrdup("phy_id", GFP_KERNEL); + phy_id_property->value = kzalloc(len, GFP_KERNEL); + + if (! phy_id_property->name) + goto err_out; + + if (! phy_id_property->value) + goto err_out; + + memcpy(phy_id_property->value, old_phy_p, len); + + phy_id_p = (__be32 *) phy_id_property->value + 1; + + *phy_id_p = cpu_to_be32(addr); + + of_update_property(slave_p, phy_id_property); + pr_info("davinci_mdio: dt: updated phy_id[%d] from phy_mask[%x]\n", addr, phy_mask); + + ++addr; + } +#if IS_ENABLED(CONFIG_OF_OVERLAY) + } +#endif + } + } + + return; + +err_out: + + if (phy_id_property) { + if (phy_id_property->name) + kfree(phy_id_property->name); + + if (phy_id_property->value) + kfree(phy_id_property->value); + + if (phy_id_property) + kfree(phy_id_property); + } +} #endif #ifdef CONFIG_PM_SLEEP From 38f4b156d991badcfc503a9e7e54226945c5d9ef Mon Sep 17 00:00:00 2001 From: Will Eccles Date: Mon, 24 May 2021 12:48:51 -0400 Subject: [PATCH 39/65] cpsw: fix undefined function with PM disabled When CONFIG_PM is not enabled, the function davinci_mdio_update_dt_from_phymask is not defined. This patch fixes this so that the build does not fail with CONFIG_PM disabled. Signed-off-by: Will Eccles --- drivers/net/ethernet/ti/davinci_mdio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/ti/davinci_mdio.c b/drivers/net/ethernet/ti/davinci_mdio.c index a508ad41c630b..67558c029b0c9 100644 --- a/drivers/net/ethernet/ti/davinci_mdio.c +++ b/drivers/net/ethernet/ti/davinci_mdio.c @@ -704,6 +704,8 @@ static int davinci_mdio_runtime_resume(struct device *dev) } return 0; } +#endif + static void davinci_mdio_update_dt_from_phymask(u32 phy_mask) { int i, len, skip; @@ -791,7 +793,6 @@ static void davinci_mdio_update_dt_from_phymask(u32 phy_mask) kfree(phy_id_property); } } -#endif #ifdef CONFIG_PM_SLEEP static int davinci_mdio_suspend(struct device *dev) From dbf99d350e7880c301387a68b2c86046414f69a5 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Thu, 16 Jul 2015 17:24:57 -0500 Subject: [PATCH 40/65] quiet: 8250_omap.c use pr_info over pr_err Signed-off-by: Robert Nelson --- drivers/tty/serial/8250/8250_omap.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 2bc6b982238fa..0b0dc70f9f430 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -1775,10 +1775,10 @@ static int __init omap8250_console_fixup(void) } add_preferred_console("ttyS", idx, options); - pr_err("WARNING: Your 'console=ttyO%d' has been replaced by 'ttyS%d'\n", + pr_info("WARNING: Your 'console=ttyO%d' has been replaced by 'ttyS%d'\n", idx, idx); - pr_err("This ensures that you still see kernel messages. Please\n"); - pr_err("update your kernel commandline.\n"); + pr_info("This ensures that you still see kernel messages. Please\n"); + pr_info("update your kernel commandline.\n"); return 0; } console_initcall(omap8250_console_fixup); From 36d8a49a90affdc05a15dcafcabc6d1e2006c808 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Thu, 20 May 2021 16:51:43 -0500 Subject: [PATCH 41/65] Revert "Revert "serial: 8250: Fix clearing FIFOs in RS485 mode again"" This reverts commit 3c9dc275dba1124c1e16e7037226038451286813. Signed-off-by: Robert Nelson --- drivers/tty/serial/8250/8250_port.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 1f231fcda657b..27ab272befa07 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -534,11 +534,30 @@ serial_port_out_sync(struct uart_port *p, int offset, int value) */ static void serial8250_clear_fifos(struct uart_8250_port *p) { + unsigned char fcr; + unsigned char clr_mask = UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT; + if (p->capabilities & UART_CAP_FIFO) { - serial_out(p, UART_FCR, UART_FCR_ENABLE_FIFO); - serial_out(p, UART_FCR, UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); - serial_out(p, UART_FCR, 0); + /* + * Make sure to avoid changing FCR[7:3] and ENABLE_FIFO bits. + * In case ENABLE_FIFO is not set, there is nothing to flush + * so just return. Furthermore, on certain implementations of + * the 8250 core, the FCR[7:3] bits may only be changed under + * specific conditions and changing them if those conditions + * are not met can have nasty side effects. One such core is + * the 8250-omap present in TI AM335x. + */ + fcr = serial_in(p, UART_FCR); + + /* FIFO is not enabled, there's nothing to clear. */ + if (!(fcr & UART_FCR_ENABLE_FIFO)) + return; + + fcr |= clr_mask; + serial_out(p, UART_FCR, fcr); + + fcr &= ~clr_mask; + serial_out(p, UART_FCR, fcr); } } @@ -1431,7 +1450,7 @@ void serial8250_em485_stop_tx(struct uart_8250_port *p) * Enable previously disabled RX interrupts. */ if (!(p->port.rs485.flags & SER_RS485_RX_DURING_TX)) { - serial8250_clear_and_reinit_fifos(p); + serial8250_clear_fifos(p); p->ier |= UART_IER_RLSI | UART_IER_RDI; serial_port_out(&p->port, UART_IER, p->ier); From 1da37419b34c496a023aa9ae1f9a66c3f27a55d7 Mon Sep 17 00:00:00 2001 From: "bigguiness@gmail.com" Date: Wed, 3 Jan 2018 15:34:07 -0800 Subject: [PATCH 42/65] ti_am335x_tsc.c driver ------=_Part_422_1349561576.1515022447432 Content-Type: text/plain; charset="UTF-8" Hello all, The TI touch screen driver does not work _right_ with the libts-bin package in the jessie image. $ cat /etc/dogtag BeagleBoard.org Debian Image 2018-01-01 $ lsb_release -a No LSB modules are available. Distributor ID: Debian Description: Debian GNU/Linux 8.10 (jessie) Release: 8.10 Codename: jessie $ dpkg -l | grep libts-bin ii libts-bin 1.14-1rcnee0~jessie+20171122 armhf touch screen library utilities $ sudo ts_calibrate ts_setup: No such file or directory It is possible to make it work by setting the TSLIB_TSDEVICE environment variable: $ sudo su # export TSLIB_TSDEVICE=/dev/input/event2 # ts_calibrate But, that's a bit of a pain since the environment variable always needs to be set in order to use the touchscreen. It appears that this version of the utilities uses the INPUT_PROP_DIRECT propbit to automatically detect which /dev/input/event device is the touchscreen. It looks like the following is the only change needed to make it work. Unfortunately, I don't have currently have a way to build a custom kernel for the BeagleBone in order to test it. If there is anyone that could I would appreciate it. Regards, Hartley --- drivers/input/touchscreen/ti_am335x_tsc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index 83e685557a197..4e0f28e0b548a 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -459,6 +459,7 @@ static int titsc_probe(struct platform_device *pdev) input_dev->name = "ti-tsc"; input_dev->dev.parent = &pdev->dev; + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); From aef90571eb2ee7f9826ef144084c659a959c0783 Mon Sep 17 00:00:00 2001 From: frost Date: Fri, 12 May 2017 00:22:29 +0800 Subject: [PATCH 43/65] ti_am335x_tsc: correct formula code to calculate pressure; fix touchscreen jitter problem --- drivers/input/touchscreen/ti_am335x_tsc.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index 4e0f28e0b548a..dd358bff4a55e 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -34,6 +34,7 @@ #define ADCFSM_STEPID 0x10 #define SEQ_SETTLE 275 #define MAX_12BIT ((1 << 12) - 1) +#define PRESSURE_MAX 1000 #define TSC_IRQENB_MASK (IRQENB_FIFO0THRES | IRQENB_EOS | IRQENB_HW_PEN) @@ -234,6 +235,7 @@ static void titsc_read_coordinates(struct titsc *ts_dev, for (i = 0; i < creads; i++) { xvals[i] = titsc_readl(ts_dev, REG_FIFO0); xvals[i] &= 0xfff; + pr_debug("i %d xval %d yval %d z1 %d z2 %d\n", i, xvals[i], yvals[i], *z1, *z2); } /* @@ -312,13 +314,13 @@ static irqreturn_t titsc_irq(int irq, void *dev) * Resistance(touch) = x plate resistance * * x postion/4096 * ((z2 / z1) - 1) */ - z = z1 - z2; + z = z2 - z1; z *= x; z *= ts_dev->x_plate_resistance; - z /= z2; + z /= z1; z = (z + 2047) >> 12; - - if (z <= MAX_12BIT) { + pr_debug("x %d y %d z1 %d z2 %d z %d\n", x, y, z1, z2, z); + if (z <= PRESSURE_MAX) { input_report_abs(input_dev, ABS_X, x); input_report_abs(input_dev, ABS_Y, y); input_report_abs(input_dev, ABS_PRESSURE, z); From 72463a408ef72649cf3806de80f02a76b812b2ae Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Mon, 12 Jul 2021 14:23:07 -0500 Subject: [PATCH 44/65] gpio-of-helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Authors: Pantelis Antoniou Charles Steinkuehler Jason Kridner Robert Nelson Tobias Müller Matthijs van Duin This patch was derived from 19 commits: https://github.com/RobertCNelson/linux-dev/tree/35e301ae8436e9f56f65bf1a7440021eda42f948/patches/drivers/ti/gpio Signed-off-by: Robert Nelson --- drivers/gpio/Kconfig | 14 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-of-helper.c | 420 ++++++++++++++++++++++++++++++++++ drivers/gpio/gpiolib-sysfs.c | 35 ++- 4 files changed, 467 insertions(+), 3 deletions(-) create mode 100644 drivers/gpio/gpio-of-helper.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index f6312ff11a0f7..86f07e0da5505 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -94,6 +94,20 @@ config GPIO_CDEV_V1 If unsure, say Y. +config GPIO_OF_HELPER + bool "GPIO OF helper device (EXPERIMENTAL)" + depends on OF_GPIO + help + Say Y here to add an GPIO OF helper driver + + Allows you specify a GPIO helper based on OF + which allows simple export of GPIO functionality + in user-space. + + Features include, value set/get, direction control, + interrupt/value change poll support, event counting + and others. + config GPIO_GENERIC depends on HAS_IOMEM # Only for IOMEM drivers tristate diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 226039b7d6ae2..d60a8564e23ae 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_OF_GPIO) += gpiolib-of.o obj-$(CONFIG_GPIO_CDEV) += gpiolib-cdev.o obj-$(CONFIG_GPIO_SYSFS) += gpiolib-sysfs.o obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o +obj-$(CONFIG_GPIO_OF_HELPER) += gpio-of-helper.o # Device drivers. Generally keep list sorted alphabetically obj-$(CONFIG_GPIO_REGMAP) += gpio-regmap.o diff --git a/drivers/gpio/gpio-of-helper.c b/drivers/gpio/gpio-of-helper.c new file mode 100644 index 0000000000000..54479fca07e95 --- /dev/null +++ b/drivers/gpio/gpio-of-helper.c @@ -0,0 +1,420 @@ +/* + * GPIO OF based helper + * + * A simple DT based driver to provide access to GPIO functionality + * to user-space via sysfs. + * + * Copyright (C) 2013 Pantelis Antoniou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* fwd decl. */ +struct gpio_of_helper_info; + +enum gpio_type { + GPIO_TYPE_INPUT = 0, + GPIO_TYPE_OUTPUT = 1, +}; + +struct gpio_of_entry { + int id; + struct gpio_of_helper_info *info; + struct device_node *node; + enum gpio_type type; + int gpio; + int irq; + const char *name; + atomic64_t counter; + unsigned int count_flags; +#define COUNT_RISING_EDGE (1 << 0) +#define COUNT_FALLING_EDGE (1 << 1) +}; + +struct gpio_of_helper_info { + struct platform_device *pdev; + struct idr idr; +}; + +static const struct of_device_id gpio_of_helper_of_match[] = { + { + .compatible = "gpio-of-helper", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, gpio_of_helper_of_match); + +static ssize_t gpio_of_helper_show_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct gpio_of_helper_info *info = platform_get_drvdata(pdev); + struct gpio_of_entry *entry; + char *p, *e; + int id, n; + + p = buf; + e = p + PAGE_SIZE; + n = 0; + idr_for_each_entry(&info->idr, entry, id) { + switch (entry->type) { + case GPIO_TYPE_INPUT: + n = snprintf(p, e - p, "%2d %-24s %3d %-3s %llu\n", + entry->id, entry->name, entry->gpio, "IN", + (unsigned long long) + atomic64_read(&entry->counter)); + break; + case GPIO_TYPE_OUTPUT: + n = snprintf(p, e - p, "%2d %-24s %3d %-3s\n", + entry->id, entry->name, entry->gpio, "OUT"); + break; + } + p += n; + } + + return p - buf; +} + +static DEVICE_ATTR(status, S_IRUGO, + gpio_of_helper_show_status, NULL); + +static irqreturn_t gpio_of_helper_handler(int irq, void *ptr) +{ + struct gpio_of_entry *entry = ptr; + + /* caution - low speed interfaces only! */ + atomic64_inc(&entry->counter); + + return IRQ_HANDLED; +} + +static struct gpio_of_entry * +gpio_of_entry_create(struct gpio_of_helper_info *info, + struct device_node *node) +{ + struct platform_device *pdev = info->pdev; + struct device *dev = &pdev->dev; + struct gpio_of_entry *entry; + int err, gpio, irq; + unsigned int req_flags, count_flags, irq_flags; + enum gpio_type type; + enum of_gpio_flags gpio_flags; + const char *name; + + /* get the type of the node first */ + if (of_property_read_bool(node, "input")) + type = GPIO_TYPE_INPUT; + else if (of_property_read_bool(node, "output") + || of_property_read_bool(node, "init-low") + || of_property_read_bool(node, "init-high")) + type = GPIO_TYPE_OUTPUT; + else { + dev_err(dev, "Not valid gpio node type\n"); + err = -EINVAL; + goto err_bad_node; + } + + /* get the name */ + if (of_property_read_string(node, "line-name", &name)) + if (of_property_read_string(node, "gpio-name", &name)) + name = node->name; + + err = of_get_named_gpio_flags(node, "gpio", 0, &gpio_flags); + if (IS_ERR_VALUE(err)) { + dev_err(dev, "Failed to get gpio property of '%s'\n", name); + goto err_bad_node; + } + gpio = err; + + req_flags = 0; + count_flags = 0; + + /* set the request flags */ + switch (type) { + case GPIO_TYPE_INPUT: + req_flags = GPIOF_DIR_IN | GPIOF_EXPORT; + if (of_property_read_bool(node, "count-falling-edge")) + count_flags |= COUNT_FALLING_EDGE; + if (of_property_read_bool(node, "count-rising-edge")) + count_flags |= COUNT_RISING_EDGE; + break; + case GPIO_TYPE_OUTPUT: + req_flags = GPIOF_DIR_OUT | GPIOF_EXPORT; + if (of_property_read_bool(node, "init-high")) + req_flags |= GPIOF_OUT_INIT_HIGH; + else if (of_property_read_bool(node, "init-low")) + req_flags |= GPIOF_OUT_INIT_LOW; + break; + } + if (of_property_read_bool(node, "dir-changeable")) + req_flags |= GPIOF_EXPORT_CHANGEABLE; + if (gpio_flags & OF_GPIO_ACTIVE_LOW) + req_flags |= GPIOF_ACTIVE_LOW; + if (gpio_flags & OF_GPIO_SINGLE_ENDED) { + if (gpio_flags & OF_GPIO_ACTIVE_LOW) + req_flags |= GPIOF_OPEN_DRAIN; + else + req_flags |= GPIOF_OPEN_SOURCE; + } + + /* request the gpio */ + err = devm_gpio_request_one(dev, gpio, req_flags, name); + if (err != 0) { + dev_err(dev, "Failed to request gpio '%s'\n", name); + goto err_bad_node; + } + + irq = -1; + irq_flags = 0; + + /* counter mode requested - need an interrupt */ + if (count_flags != 0) { + irq = gpio_to_irq(gpio); + if (IS_ERR_VALUE(irq)) { + dev_err(dev, "Failed to request gpio '%s'\n", name); + goto err_bad_node; + } + + if (count_flags & COUNT_RISING_EDGE) + irq_flags |= IRQF_TRIGGER_RISING; + if (count_flags & COUNT_FALLING_EDGE) + irq_flags |= IRQF_TRIGGER_FALLING; + } + + idr_preload(GFP_KERNEL); + + entry = devm_kzalloc(dev, sizeof(*entry), GFP_KERNEL); + if (entry == NULL) { + dev_err(dev, "Failed to allocate gpio entry of '%s'\n", name); + err = -ENOMEM; + goto err_no_mem; + } + + entry->id = -1; + entry->info = info; + entry->node = of_node_get(node); /* get node reference */ + entry->type = type; + entry->gpio = gpio; + entry->irq = irq; + entry->name = name; + + /* interrupt enable is last thing done */ + if (irq >= 0) { + atomic64_set(&entry->counter, 0); + entry->count_flags = count_flags; + err = devm_request_irq(dev, irq, gpio_of_helper_handler, + irq_flags, name, entry); + if (err != 0) { + dev_err(dev, "Failed to request irq of '%s'\n", name); + goto err_no_irq; + } + } + + err = idr_alloc(&info->idr, entry, 0, 0, GFP_NOWAIT); + if (err >= 0) + entry->id = err; + + idr_preload_end(); + + if (err < 0) { + dev_err(dev, "Failed to idr_get_new of '%s'\n", name); + goto err_fail_idr; + } + + dev_info(dev, "Allocated GPIO id=%d name='%s'\n", entry->id, name); + + return entry; + +err_fail_idr: + /* nothing to do */ +err_no_irq: + /* release node ref */ + of_node_put(node); + /* nothing else needs to be done, devres handles it */ +err_no_mem: +err_bad_node: + return ERR_PTR(err); +} + +static int gpio_of_entry_destroy(struct gpio_of_entry *entry) +{ + struct gpio_of_helper_info *info = entry->info; + struct platform_device *pdev = info->pdev; + struct device *dev = &pdev->dev; + + dev_dbg(dev, "Destroying GPIO id=%d\n", entry->id); + + /* remove from the IDR */ + idr_remove(&info->idr, entry->id); + + /* remove node ref */ + of_node_put(entry->node); + + /* free gpio */ + devm_gpio_free(dev, entry->gpio); + + /* gree irq */ + if (entry->irq >= 0) + devm_free_irq(dev, entry->irq, entry); + + /* and free */ + devm_kfree(dev, entry); + + return 0; +} + +static int gpio_of_helper_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct gpio_of_helper_info *info; + struct gpio_of_entry *entry; + struct device_node *pnode = pdev->dev.of_node; + struct device_node *cnode; + struct pinctrl *pinctrl; + int err; + + /* we only support OF */ + if (pnode == NULL) { + dev_err(&pdev->dev, "No platform of_node!\n"); + return -ENODEV; + } + + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) { + /* special handling for probe defer */ + if (PTR_ERR(pinctrl) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + dev_warn(&pdev->dev, + "pins are not configured from the driver\n"); + } + + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (info == NULL) { + dev_err(&pdev->dev, "Failed to allocate info\n"); + err = -ENOMEM; + goto err_no_mem; + } + platform_set_drvdata(pdev, info); + info->pdev = pdev; + + idr_init(&info->idr); + + err = device_create_file(dev, &dev_attr_status); + if (err != 0) { + dev_err(dev, "Failed to create status sysfs attribute\n"); + goto err_no_sysfs; + } + + for_each_child_of_node(pnode, cnode) { + + entry = gpio_of_entry_create(info, cnode); + if (IS_ERR_OR_NULL(entry)) { + dev_err(dev, "Failed to create gpio entry\n"); + err = PTR_ERR(entry); + goto err_fail_entry; + } + } + + dev_info(&pdev->dev, "ready\n"); + + return 0; +err_fail_entry: + device_remove_file(&pdev->dev, &dev_attr_status); +err_no_sysfs: +err_no_mem: + return err; +} + +static int gpio_of_helper_remove(struct platform_device *pdev) +{ + struct gpio_of_helper_info *info = platform_get_drvdata(pdev); + struct gpio_of_entry *entry; + int id; + + dev_info(&pdev->dev, "removing\n"); + + device_remove_file(&pdev->dev, &dev_attr_status); + + id = 0; + idr_for_each_entry(&info->idr, entry, id) { + /* destroy each and every one */ + gpio_of_entry_destroy(entry); + } + + return 0; +} + +#ifdef CONFIG_PM +static int gpio_of_helper_runtime_suspend(struct device *dev) +{ + /* place holder */ + return 0; +} + +static int gpio_of_helper_runtime_resume(struct device *dev) +{ + /* place holder */ + return 0; +} + +static struct dev_pm_ops gpio_of_helper_pm_ops = { + SET_RUNTIME_PM_OPS(gpio_of_helper_runtime_suspend, + gpio_of_helper_runtime_resume, NULL) +}; +#define GPIO_OF_HELPER_PM_OPS (&gpio_of_helper_pm_ops) +#else +#define GPIO_OF_HELPER_PM_OPS NULL +#endif /* CONFIG_PM */ + +struct platform_driver gpio_of_helper_driver = { + .probe = gpio_of_helper_probe, + .remove = gpio_of_helper_remove, + .driver = { + .name = "gpio-of-helper", + .owner = THIS_MODULE, + .pm = GPIO_OF_HELPER_PM_OPS, + .of_match_table = gpio_of_helper_of_match, + }, +}; + +module_platform_driver(gpio_of_helper_driver); + +MODULE_AUTHOR("Pantelis Antoniou "); +MODULE_DESCRIPTION("GPIO OF Helper driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:gpio-of-helper"); diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index fa5d945b2f286..2a7b2c6388fb6 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -38,10 +38,10 @@ static DEFINE_MUTEX(sysfs_lock); /* * /sys/class/gpio/gpioN... only for GPIOs that are exported * /direction - * * MAY BE OMITTED if kernel won't allow direction changes * * is read/write as "in" or "out" * * may also be written as "high" or "low", initializing * output value as specified ("out" implies "low") + * * read-only if kernel won't allow direction changes * /value * * always readable, subject to hardware behavior * * may be writable, as zero/nonzero @@ -54,6 +54,8 @@ static DEFINE_MUTEX(sysfs_lock); * * is read/write as zero/nonzero * * also affects existing and subsequent "falling" and "rising" * /edge configuration + * /label + * * descriptor label */ static ssize_t direction_show(struct device *dev, @@ -84,7 +86,9 @@ static ssize_t direction_store(struct device *dev, mutex_lock(&data->mutex); - if (sysfs_streq(buf, "high")) + if (!data->direction_can_change) + status = -EPERM; + else if (sysfs_streq(buf, "high")) status = gpiod_direction_output_raw(desc, 1); else if (sysfs_streq(buf, "out") || sysfs_streq(buf, "low")) status = gpiod_direction_output_raw(desc, 0); @@ -363,6 +367,23 @@ static ssize_t active_low_store(struct device *dev, } static DEVICE_ATTR_RW(active_low); +static ssize_t label_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpiod_data *data = dev_get_drvdata(dev); + struct gpio_desc *desc = data->desc; + ssize_t status; + + mutex_lock(&data->mutex); + + status = sprintf(buf, "%s\n", desc->label); + + mutex_unlock(&data->mutex); + + return status; +} +static DEVICE_ATTR_RO(label); + static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr, int n) { @@ -374,12 +395,15 @@ static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr, if (attr == &dev_attr_direction.attr) { if (!show_direction) - mode = 0; + mode &= 0444; } else if (attr == &dev_attr_edge.attr) { if (gpiod_to_irq(desc) < 0) mode = 0; if (!show_direction && test_bit(FLAG_IS_OUT, &desc->flags)) mode = 0; + } else if (attr == &dev_attr_value.attr) { + if (!show_direction && !test_bit(FLAG_IS_OUT, &desc->flags)) + mode &= 0444; } return mode; @@ -390,6 +414,7 @@ static struct attribute *gpio_attrs[] = { &dev_attr_edge.attr, &dev_attr_value.attr, &dev_attr_active_low.attr, + &dev_attr_label.attr, NULL, }; @@ -403,6 +428,10 @@ static const struct attribute_group *gpio_groups[] = { NULL }; +/* bwlegh, a second device in the same file... get out of my namespace! */ +#define dev_attr_label dev_attr_chip_label +#define label_show chip_label_show + /* * /sys/class/gpio/gpiochipN/ * /base ... matching gpio_chip.base (N) From c769ff8be9b700ae42a21fd369815e3c53934dc3 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Mon, 12 Jul 2021 14:35:03 -0500 Subject: [PATCH 45/65] bone-pinmux-helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Authors: Pantelis Antoniou Charles Steinkuehler Jason Kridner Robert Nelson Tobias Müller Matthijs van Duin This patch was derived from 19 commits: https://github.com/RobertCNelson/linux-dev/tree/35e301ae8436e9f56f65bf1a7440021eda42f948/patches/drivers/ti/gpio Signed-off-by: Robert Nelson --- drivers/misc/Kconfig | 1 + drivers/misc/Makefile | 1 + drivers/misc/cape/Kconfig | 5 + drivers/misc/cape/Makefile | 5 + drivers/misc/cape/beaglebone/Kconfig | 10 + drivers/misc/cape/beaglebone/Makefile | 5 + .../misc/cape/beaglebone/bone-pinmux-helper.c | 242 ++++++++++++++++++ 7 files changed, 269 insertions(+) create mode 100644 drivers/misc/cape/Kconfig create mode 100644 drivers/misc/cape/Makefile create mode 100644 drivers/misc/cape/beaglebone/Kconfig create mode 100644 drivers/misc/cape/beaglebone/Makefile create mode 100644 drivers/misc/cape/beaglebone/bone-pinmux-helper.c diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 501dde250309f..bb0cadf6ac23e 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -487,6 +487,7 @@ source "drivers/misc/altera-stapl/Kconfig" source "drivers/misc/mei/Kconfig" source "drivers/misc/vmw_vmci/Kconfig" source "drivers/misc/genwqe/Kconfig" +source "drivers/misc/cape/Kconfig" source "drivers/misc/echo/Kconfig" source "drivers/misc/cxl/Kconfig" source "drivers/misc/ocxl/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 4b0ac87c76311..d8a4c3d095b68 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_SRAM) += sram.o obj-$(CONFIG_SRAM_EXEC) += sram-exec.o obj-$(CONFIG_SRAM_DMA_HEAP) += sram-dma-heap.o obj-$(CONFIG_GENWQE) += genwqe/ +obj-y += cape/ obj-$(CONFIG_ECHO) += echo/ obj-$(CONFIG_CXL_BASE) += cxl/ obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o diff --git a/drivers/misc/cape/Kconfig b/drivers/misc/cape/Kconfig new file mode 100644 index 0000000000000..a2ef85e0415c5 --- /dev/null +++ b/drivers/misc/cape/Kconfig @@ -0,0 +1,5 @@ +# +# Capes +# + +source "drivers/misc/cape/beaglebone/Kconfig" diff --git a/drivers/misc/cape/Makefile b/drivers/misc/cape/Makefile new file mode 100644 index 0000000000000..7c4eb96ab29e4 --- /dev/null +++ b/drivers/misc/cape/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for cape like devices +# + +obj-y += beaglebone/ diff --git a/drivers/misc/cape/beaglebone/Kconfig b/drivers/misc/cape/beaglebone/Kconfig new file mode 100644 index 0000000000000..eeb67828fd829 --- /dev/null +++ b/drivers/misc/cape/beaglebone/Kconfig @@ -0,0 +1,10 @@ +# +# Beaglebone capes +# + +config BEAGLEBONE_PINMUX_HELPER + tristate "Beaglebone Pinmux Helper" + depends on ARCH_OMAP2PLUS && OF + default n + help + Say Y here to include support for the pinmux helper diff --git a/drivers/misc/cape/beaglebone/Makefile b/drivers/misc/cape/beaglebone/Makefile new file mode 100644 index 0000000000000..7f4617a01e262 --- /dev/null +++ b/drivers/misc/cape/beaglebone/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for beaglebone capes +# + +obj-$(CONFIG_BEAGLEBONE_PINMUX_HELPER) += bone-pinmux-helper.o diff --git a/drivers/misc/cape/beaglebone/bone-pinmux-helper.c b/drivers/misc/cape/beaglebone/bone-pinmux-helper.c new file mode 100644 index 0000000000000..d81363a77b103 --- /dev/null +++ b/drivers/misc/cape/beaglebone/bone-pinmux-helper.c @@ -0,0 +1,242 @@ +/* + * Pinmux helper driver + * + * Copyright (C) 2013 Pantelis Antoniou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct of_device_id bone_pinmux_helper_of_match[] = { + { + .compatible = "bone-pinmux-helper", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, bone_pinmux_helper_of_match); + +struct pinmux_helper_data { + struct pinctrl *pinctrl; + char *selected_state_name; +}; + +static ssize_t pinmux_helper_show_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pinmux_helper_data *data = platform_get_drvdata(pdev); + const char *name; + + name = data->selected_state_name; + if (name == NULL || strlen(name) == 0) + name = "none"; + return sprintf(buf, "%s\n", name); +} + +static ssize_t pinmux_helper_store_state(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pinmux_helper_data *data = platform_get_drvdata(pdev); + struct pinctrl_state *state; + char *state_name; + char *s; + int err; + + /* duplicate (as a null terminated string) */ + state_name = kmalloc(count + 1, GFP_KERNEL); + if (state_name == NULL) + return -ENOMEM; + memcpy(state_name, buf, count); + state_name[count] = '\0'; + + /* and chop off newline */ + s = strchr(state_name, '\n'); + if (s != NULL) + *s = '\0'; + + /* try to select default state at first (if it exists) */ + state = pinctrl_lookup_state(data->pinctrl, state_name); + if (!IS_ERR(state)) { + err = pinctrl_select_state(data->pinctrl, state); + if (err != 0) + dev_err(dev, "Failed to select state %s\n", + state_name); + } else { + dev_err(dev, "Failed to find state %s\n", state_name); + err = PTR_RET(state); + } + + if (err == 0) { + kfree(data->selected_state_name); + data->selected_state_name = state_name; + } + + return err ? err : count; +} + +static DEVICE_ATTR(state, S_IWUSR | S_IRUGO, + pinmux_helper_show_state, pinmux_helper_store_state); + +static struct attribute *pinmux_helper_attributes[] = { + &dev_attr_state.attr, + NULL +}; + +static const struct attribute_group pinmux_helper_attr_group = { + .attrs = pinmux_helper_attributes, +}; + +static int bone_pinmux_helper_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pinmux_helper_data *data; + struct pinctrl_state *state; + char *state_name; + const char *mode_name; + int mode_len; + int err; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (data == NULL) { + dev_err(dev, "Failed to allocate data\n"); + err = -ENOMEM; + goto err_no_mem; + } + + state_name = kmalloc(strlen(PINCTRL_STATE_DEFAULT) + 1, + GFP_KERNEL); + if (state_name == NULL) { + dev_err(dev, "Failed to allocate state name\n"); + err = -ENOMEM; + goto err_no_state_mem; + } + data->selected_state_name = state_name; + strcpy(data->selected_state_name, PINCTRL_STATE_DEFAULT); + + platform_set_drvdata(pdev, data); + + data->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR(data->pinctrl)) { + dev_err(dev, "Failed to get pinctrl\n"); + err = PTR_RET(data->pinctrl); + goto err_no_pinctrl; + } + + /* See if an initial mode is specified in the device tree */ + mode_name = of_get_property(dev->of_node, "mode", &mode_len); + + err = -1; + if (mode_name != NULL ) { + state_name = kmalloc(mode_len + 1, GFP_KERNEL); + if (state_name == NULL) { + dev_err(dev, "Failed to allocate state name\n"); + err = -ENOMEM; + goto err_no_mode_mem; + } + strncpy(state_name, mode_name, mode_len); + + /* try to select requested mode */ + state = pinctrl_lookup_state(data->pinctrl, state_name); + if (!IS_ERR(state)) { + err = pinctrl_select_state(data->pinctrl, state); + if (err != 0) { + dev_warn(dev, "Unable to select requested mode %s\n", state_name); + kfree(state_name); + } else { + kfree(data->selected_state_name); + data->selected_state_name = state_name; + dev_notice(dev, "Set initial pinmux mode to %s\n", state_name); + } + } + } + + /* try to select default state if mode_name failed */ + if ( err != 0) { + state = pinctrl_lookup_state(data->pinctrl, + data->selected_state_name); + if (!IS_ERR(state)) { + err = pinctrl_select_state(data->pinctrl, state); + if (err != 0) { + dev_err(dev, "Failed to select default state\n"); + goto err_no_state; + } + } else { + data->selected_state_name = '\0'; + } + } + + /* Register sysfs hooks */ + err = sysfs_create_group(&dev->kobj, &pinmux_helper_attr_group); + if (err) { + dev_err(dev, "Failed to create sysfs group\n"); + goto err_no_sysfs; + } + + return 0; + +err_no_sysfs: +err_no_state: +err_no_mode_mem: + devm_pinctrl_put(data->pinctrl); +err_no_pinctrl: + devm_kfree(dev, data->selected_state_name); +err_no_state_mem: + devm_kfree(dev, data); +err_no_mem: + return err; +} + +static int bone_pinmux_helper_remove(struct platform_device *pdev) +{ + struct pinmux_helper_data *data = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + sysfs_remove_group(&dev->kobj, &pinmux_helper_attr_group); + kfree(data->selected_state_name); + devm_pinctrl_put(data->pinctrl); + devm_kfree(dev, data); + + return 0; +} + +struct platform_driver bone_pinmux_helper_driver = { + .probe = bone_pinmux_helper_probe, + .remove = bone_pinmux_helper_remove, + .driver = { + .name = "bone-pinmux-helper", + .owner = THIS_MODULE, + .of_match_table = bone_pinmux_helper_of_match, + }, +}; + +module_platform_driver(bone_pinmux_helper_driver); + +MODULE_AUTHOR("Pantelis Antoniou"); +MODULE_DESCRIPTION("Beaglebone pinmux helper driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:bone-pinmux-helper"); From 8faddc111ad7dff8dcac2df085e447949569513f Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Sun, 30 Dec 2018 19:44:02 -0600 Subject: [PATCH 46/65] hack: gpiolib: yes we have drivers stomping on each other, we need to find a better way to share gpio... Signed-off-by: Robert Nelson --- drivers/gpio/gpiolib.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 5c94d02ee42a5..2f7405822590f 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2021,10 +2021,10 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) { desc_set_label(desc, label ? : "?"); ret = 0; - } else { - kfree_const(label); - ret = -EBUSY; - goto done; +// } else { +// kfree_const(label); +// ret = -EBUSY; +// goto done; } if (gc->request) { From 80d08a2e7cc041ec729022483a201e87654aef33 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Tue, 21 Apr 2020 18:33:57 -0500 Subject: [PATCH 47/65] v5.7-rc2: bone-pinmux-helper: Replace PTR_RET with PTR_ERR_OR_ZERO Signed-off-by: Robert Nelson --- drivers/misc/cape/beaglebone/bone-pinmux-helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/cape/beaglebone/bone-pinmux-helper.c b/drivers/misc/cape/beaglebone/bone-pinmux-helper.c index d81363a77b103..57703d8bcac7c 100644 --- a/drivers/misc/cape/beaglebone/bone-pinmux-helper.c +++ b/drivers/misc/cape/beaglebone/bone-pinmux-helper.c @@ -88,7 +88,7 @@ static ssize_t pinmux_helper_store_state(struct device *dev, state_name); } else { dev_err(dev, "Failed to find state %s\n", state_name); - err = PTR_RET(state); + err = PTR_ERR_OR_ZERO(state); } if (err == 0) { @@ -143,7 +143,7 @@ static int bone_pinmux_helper_probe(struct platform_device *pdev) data->pinctrl = devm_pinctrl_get(dev); if (IS_ERR(data->pinctrl)) { dev_err(dev, "Failed to get pinctrl\n"); - err = PTR_RET(data->pinctrl); + err = PTR_ERR_OR_ZERO(data->pinctrl); goto err_no_pinctrl; } From ece60a9d4eb4edc361307a6ca3cc7764a0187bf2 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Mon, 13 Dec 2021 11:28:09 -0600 Subject: [PATCH 48/65] gpio-of-helper.c: espect the status property of individual gpios # is there a kernel option to not reset gpio state at linux boot ? # Daulity: the kernel does not reset any gpio unless explicitly requested to by a driver # though by default if cape-universal is enabled then a gpio-of-helper device gets set up which configures all gpios as input... though that is normally the state they are in anyway after reset # Daulity: why? are you setting gpios in u-boot? # yes u-boot sets a few gpio's before it boots the kernel they get reset to a certain state not certain if u-boot or linux kernel # was just wondering # the annoying bit is that this isn't really fixable by applying an overlay on top of cape-universal due the the limitations of overlays and the fact that status="disabled"; doesn't work on individual gpios of a gpio-of-helper device node # so your options are to modify the cape-universal overlay or disable cape-universal entirely and use an overlay to declare/export gpios (with initialization of your choice) # i see # (or fix the gpio-of-helper drivers to respect the status property of individual gpios... which is probably a 2-line patch) # *driver # interesting, if CONFIG_OF_KOBJ=n then nodes with non-okay status property don't even get deserialized, however in practice CONFIG_OF_KOBJ is always y (specifically, it is only n in kernels that lack sysfs support) # yeah it's definitely a 2-line fix # https://pastebin.com/f8V8pz1V # thanks :) # rcn-ee: can you include that patch? that way overlays can disable cape-universal's gpio export for individual gpios used by the overlay # e.g. &ocp { cape-universal { P9_14 { status = "disabled"; }; }; }; # Daulity: you can use that in an overlay and then if you still want the gpio exported you can just declare your own gpio-of-helper ... unfortunately it doesn't support exporting a gpio without initializing it, but at least you can choose *how* to initialize it (input, output-low, output-high) and whether or not linux userspace is allowed to change the direction of the gpio Signed-off-by: Robert Nelson --- drivers/gpio/gpio-of-helper.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpio/gpio-of-helper.c b/drivers/gpio/gpio-of-helper.c index 54479fca07e95..21af24aeaf65c 100644 --- a/drivers/gpio/gpio-of-helper.c +++ b/drivers/gpio/gpio-of-helper.c @@ -341,6 +341,8 @@ static int gpio_of_helper_probe(struct platform_device *pdev) } for_each_child_of_node(pnode, cnode) { + if (!of_device_is_available(cnode)) + continue; entry = gpio_of_entry_create(info, cnode); if (IS_ERR_OR_NULL(entry)) { From bbd7931627ce8888b762ba530a937aaa0493e7f7 Mon Sep 17 00:00:00 2001 From: Jason Kridner Date: Fri, 23 Oct 2020 18:58:13 +0530 Subject: [PATCH 49/65] staging: fbtft: fb_ssd1306 add 96x39 oled support Original Commit : https://github.com/beagleboard/linux/commit/8884f22a6b3a5217177c2f843e5c83d613bc6676 Signed-off-by: Jason Kridner --- drivers/staging/fbtft/fb_ssd1306.c | 59 +++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/drivers/staging/fbtft/fb_ssd1306.c b/drivers/staging/fbtft/fb_ssd1306.c index 6cf9df579e88b..4cd61617154df 100644 --- a/drivers/staging/fbtft/fb_ssd1306.c +++ b/drivers/staging/fbtft/fb_ssd1306.c @@ -55,6 +55,9 @@ static int init_display(struct fbtft_par *par) write_reg(par, 0x3F); else if (par->info->var.yres == 48) write_reg(par, 0x2F); + else if (par->info->var.yres == 39) + /* https://libstock.mikroe.com/projects/download/1111/2577/1411057038_oled_b_click___e_mikroc_arm.zip */ + write_reg(par, 0x27); else write_reg(par, 0x1F); @@ -76,19 +79,27 @@ static int init_display(struct fbtft_par *par) write_reg(par, 0x01); /* Set Segment Re-map */ - /* column address 127 is mapped to SEG0 */ - write_reg(par, 0xA0 | 0x1); + if (par->info->var.yres == 39) + /* no segment re-map */ + write_reg(par, 0xA0 | 0x0); + else + /* column address 127 is mapped to SEG0 */ + write_reg(par, 0xA0 | 0x1); /* Set COM Output Scan Direction */ - /* remapped mode. Scan from COM[N-1] to COM0 */ - write_reg(par, 0xC8); + if (par->info->var.yres == 39) + /* no columnt re-map mode. Scan from COM0 to COM[N-1] */ + write_reg(par, 0xC0); + else + /* remapped mode. Scan from COM[N-1] to COM0 */ + write_reg(par, 0xC8); /* Set COM Pins Hardware Configuration */ write_reg(par, 0xDA); if (par->info->var.yres == 64) /* A[4]=1b, Alternative COM pin configuration */ write_reg(par, 0x12); - else if (par->info->var.yres == 48) + else if (par->info->var.yres == 48 || par->info->var.yres == 39) /* A[4]=1b, Alternative COM pin configuration */ write_reg(par, 0x12); else @@ -97,12 +108,18 @@ static int init_display(struct fbtft_par *par) /* Set Pre-charge Period */ write_reg(par, 0xD9); - write_reg(par, 0xF1); + if (par->info->var.yres == 39) + write_reg(par, 0x25); + else + write_reg(par, 0xF1); /* Set VCOMH Deselect Level */ write_reg(par, 0xDB); - /* according to the datasheet, this value is out of bounds */ - write_reg(par, 0x40); + if (par->info->var.yres == 39) + write_reg(par, 0x20); + else + /* according to the datasheet, this value is out of bounds */ + write_reg(par, 0x40); /* Entire Display ON */ /* Resume to RAM content display. Output follows RAM content */ @@ -133,6 +150,20 @@ static void set_addr_win_64x48(struct fbtft_par *par) write_reg(par, 0x5); } +static void set_addr_win_96x39(struct fbtft_par *par) +{ + /* Set Page Address */ + write_reg(par, 0xB0); + /* Set Column Address */ + write_reg(par, 0x21); + write_reg(par, 0x00); + write_reg(par, 0x5F); + /* Set Page Address Range */ + write_reg(par, 0x22); + write_reg(par, 0x0); + write_reg(par, 0x4); +} + static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) { /* Set Lower Column Start Address for Page Addressing Mode */ @@ -144,6 +175,8 @@ static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) if (par->info->var.xres == 64 && par->info->var.yres == 48) set_addr_win_64x48(par); + else if (par->info->var.xres == 96 && par->info->var.yres == 39) + set_addr_win_96x39(par); } static int blank(struct fbtft_par *par, bool on) @@ -188,11 +221,19 @@ static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) *buf |= BIT(i); buf++; } + if (yres % 8) { + *buf = 0x00; + for (i = 0; i < (yres - (y * 8)); i++) + if (vmem16[(y * 8 + i) * xres + x]) + *buf |= BIT(i); + buf++; + y++; + } } /* Write data */ gpiod_set_value(par->gpio.dc, 1); - ret = par->fbtftops.write(par, par->txbuf.buf, xres * yres / 8); + ret = par->fbtftops.write(par, par->txbuf.buf, xres * (yres / 8 + (yres % 8 != 0))); if (ret < 0) dev_err(par->info->device, "write failed and returned: %d\n", ret); From c8143cc0c55b67f9aecc5cbca58f10e985e5bb50 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Tue, 28 Sep 2021 11:33:34 -0500 Subject: [PATCH 50/65] auxdisplay: import Seeed version Signed-off-by: Robert Nelson --- drivers/auxdisplay/Kconfig | 12 + drivers/auxdisplay/Makefile | 1 + drivers/auxdisplay/seeed-hd44780-i2c.c | 713 +++++++++++++++++++++++++ drivers/auxdisplay/seeed-hd44780.h | 52 ++ 4 files changed, 778 insertions(+) create mode 100644 drivers/auxdisplay/seeed-hd44780-i2c.c create mode 100644 drivers/auxdisplay/seeed-hd44780.h diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig index 81757eeded68a..93a44635c5496 100644 --- a/drivers/auxdisplay/Kconfig +++ b/drivers/auxdisplay/Kconfig @@ -27,6 +27,18 @@ config HD44780 kernel and started at boot. If you don't understand what all this is about, say N. +config SEEED_I2C_HD44780 + tristate "SEEED I2C HD44780 Character LCD support" + depends on I2C + select CHARLCD + default n + help + Enable support for Character LCDs using a HD44780 controller. + The LCD is accessible through the /dev/lcd char device (10, 156). + This code can either be compiled as a module, or linked into the + kernel and started at boot. + If you don't understand what all this is about, say N. + config KS0108 tristate "KS0108 LCD Controller" depends on PARPORT_PC diff --git a/drivers/auxdisplay/Makefile b/drivers/auxdisplay/Makefile index cf54b5efb07e0..03a88f30b4b31 100644 --- a/drivers/auxdisplay/Makefile +++ b/drivers/auxdisplay/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_IMG_ASCII_LCD) += img-ascii-lcd.o obj-$(CONFIG_HD44780) += hd44780.o obj-$(CONFIG_HT16K33) += ht16k33.o obj-$(CONFIG_PARPORT_PANEL) += panel.o +obj-$(CONFIG_SEEED_I2C_HD44780) += seeed-hd44780-i2c.o diff --git a/drivers/auxdisplay/seeed-hd44780-i2c.c b/drivers/auxdisplay/seeed-hd44780-i2c.c new file mode 100644 index 0000000000000..7a022154280d2 --- /dev/null +++ b/drivers/auxdisplay/seeed-hd44780-i2c.c @@ -0,0 +1,713 @@ +/* + * hd44780-i2c.c + * + * Implements I2C interface for JHD1802/HD44780 LCD. + * Driver is based on driver written by gorskima + * (https://github.com/gorskima/hd44780-i2c). + * + * Port to support JHD1802 by Peter Yang + * Copyright (C) 2019 Seeed Studio + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "seeed-hd44780.h" + +#define CLASS_NAME "hd44780" +#define NAME "seeed-hd44780" +#define NUM_DEVICES 8 + +static struct class *hd44780_class; +static dev_t dev_no; +/* We start with -1 so that first returned minor is 0 */ +static atomic_t next_minor = ATOMIC_INIT(-1); + +static LIST_HEAD(hd44780_list); +static DEFINE_SPINLOCK(hd44780_list_lock); + + +#define BL 0x08 +#define E 0x04 +#define RW 0x02 +#define RS 0x01 +#define _DATA 0x40 +#define _CMD 0x80 + +#define HD44780_CLEAR_DISPLAY 0x01 +#define HD44780_RETURN_HOME 0x02 +#define HD44780_ENTRY_MODE_SET 0x04 +#define HD44780_DISPLAY_CTRL 0x08 +#define HD44780_SHIFT 0x10 +#define HD44780_FUNCTION_SET 0x20 +#define HD44780_CGRAM_ADDR 0x40 +#define HD44780_DDRAM_ADDR 0x80 + +#define HD44780_DL_8BITS 0x10 +#define HD44780_DL_4BITS 0x00 +#define HD44780_N_2LINES 0x08 +#define HD44780_N_1LINE 0x00 +#define HD44780_5x10DOTS 0x04 +#define HD44780_5x8DOTS 0x00 + +#define HD44780_D_DISPLAY_ON 0x04 +#define HD44780_D_DISPLAY_OFF 0x00 +#define HD44780_C_CURSOR_ON 0x02 +#define HD44780_C_CURSOR_OFF 0x00 +#define HD44780_B_BLINK_ON 0x01 +#define HD44780_B_BLINK_OFF 0x00 + +#define HD44780_ID_INCREMENT 0x02 +#define HD44780_ID_DECREMENT 0x00 +#define HD44780_S_SHIFT_ON 0x01 +#define HD44780_S_SHIFT_OFF 0x00 + +static struct hd44780_geometry hd44780_geometry_20x4 = { + .cols = 20, + .rows = 4, + .start_addrs = {0x00, 0x40, 0x14, 0x54}, +}; + +static struct hd44780_geometry hd44780_geometry_16x2 = { + .cols = 16, + .rows = 2, + .start_addrs = {0x00, 0x40}, +}; + +static struct hd44780_geometry hd44780_geometry_8x1 = { + .cols = 8, + .rows = 1, + .start_addrs = {0x00}, +}; + +struct hd44780_geometry *hd44780_geometries[] = { + &hd44780_geometry_20x4, + &hd44780_geometry_16x2, + &hd44780_geometry_8x1, + NULL +}; + +/* Defines possible register that we can write to */ +typedef enum { IR, DR } dest_reg; + +static void hd44780_write_nibble(struct hd44780 *lcd, dest_reg reg, u8 data) +{ + u8 first; + + /* + * First byte, select DATA or CMD + * Second byte, DATA or CMD + */ + first = (reg == DR)? _DATA: _CMD; + i2c_smbus_write_byte_data(lcd->i2c_client, first, data); + + return; +} + +/* + * JHD1802 lowlevel functions + */ +static void hd44780_write_instruction(struct hd44780 *lcd, u8 data) +{ + hd44780_write_nibble(lcd, IR, data); + udelay(37); +} + +static void hd44780_write_data(struct hd44780 *lcd, u8 data) +{ + hd44780_write_nibble(lcd, DR, data); + udelay(37 + 4); +} + +static void hd44780_write_char(struct hd44780 *lcd, char ch) +{ + struct hd44780_geometry *geo = lcd->geometry; + + hd44780_write_data(lcd, ch); + + lcd->pos.col++; + + if (lcd->pos.col == geo->cols) { + lcd->pos.row = (lcd->pos.row + 1) % geo->rows; + lcd->pos.col = 0; + hd44780_write_instruction(lcd, HD44780_DDRAM_ADDR | geo->start_addrs[lcd->pos.row]); + } +} + +static void hd44780_clear_display(struct hd44780 *lcd) +{ + hd44780_write_instruction(lcd, HD44780_CLEAR_DISPLAY); + + /* Wait for 1.64 ms because this one needs more time */ + udelay(1640); + + /* + * CLEAR_DISPLAY instruction also returns cursor to home, + * so we need to update it locally. + */ + lcd->pos.row = 0; + lcd->pos.col = 0; +} + +static void hd44780_clear_line(struct hd44780 *lcd) +{ + struct hd44780_geometry *geo; + int start_addr, col; + + geo = lcd->geometry; + start_addr = geo->start_addrs[lcd->pos.row]; + + hd44780_write_instruction(lcd, HD44780_DDRAM_ADDR | start_addr); + + for (col = 0; col < geo->cols; col++) + hd44780_write_data(lcd, ' '); + + hd44780_write_instruction(lcd, HD44780_DDRAM_ADDR | start_addr); +} + +static void hd44780_handle_new_line(struct hd44780 *lcd) +{ + struct hd44780_geometry *geo = lcd->geometry; + + lcd->pos.row = (lcd->pos.row + 1) % geo->rows; + lcd->pos.col = 0; + hd44780_write_instruction(lcd, HD44780_DDRAM_ADDR + | geo->start_addrs[lcd->pos.row]); + hd44780_clear_line(lcd); +} + +static void hd44780_handle_carriage_return(struct hd44780 *lcd) +{ + struct hd44780_geometry *geo = lcd->geometry; + + lcd->pos.col = 0; + hd44780_write_instruction(lcd, HD44780_DDRAM_ADDR + | geo->start_addrs[lcd->pos.row]); +} + +static void hd44780_leave_esc_seq(struct hd44780 *lcd) +{ + memset(lcd->esc_seq_buf.buf, 0, ESC_SEQ_BUF_SIZE); + lcd->esc_seq_buf.length = 0; + lcd->is_in_esc_seq = false; +} + +static void hd44780_flush_esc_seq(struct hd44780 *lcd) +{ + char *buf_to_flush; + int buf_length; + + /* Copy and reset current esc seq */ + buf_to_flush = kmalloc(sizeof(char) * ESC_SEQ_BUF_SIZE, GFP_KERNEL); + memcpy(buf_to_flush, lcd->esc_seq_buf.buf, ESC_SEQ_BUF_SIZE); + buf_length = lcd->esc_seq_buf.length; + + hd44780_leave_esc_seq(lcd); + + /* Write \e that initiated current esc seq */ + hd44780_write_char(lcd, '\e'); + + /* Flush current esc seq */ + hd44780_write(lcd, buf_to_flush, buf_length); + + kfree(buf_to_flush); +} + +void hd44780_flush(struct hd44780 *lcd) +{ + while (lcd->is_in_esc_seq) + hd44780_flush_esc_seq(lcd); +} +EXPORT_SYMBOL_GPL(hd44780_flush); + +static void hd44780_handle_esc_seq_char(struct hd44780 *lcd, char ch) +{ + int prev_row, prev_col; + + lcd->esc_seq_buf.buf[lcd->esc_seq_buf.length++] = ch; + + if (!strcmp(lcd->esc_seq_buf.buf, "[2J")) { + prev_row = lcd->pos.row; + prev_col = lcd->pos.col; + + hd44780_clear_display(lcd); + hd44780_write_instruction(lcd, HD44780_DDRAM_ADDR | (lcd->geometry->start_addrs[prev_row] + prev_col)); + + hd44780_leave_esc_seq(lcd); + } else if (!strcmp(lcd->esc_seq_buf.buf, "[H")) { + hd44780_write_instruction(lcd, HD44780_RETURN_HOME); + lcd->pos.row = 0; + lcd->pos.col = 0; + + hd44780_leave_esc_seq(lcd); + } else if (lcd->esc_seq_buf.length == ESC_SEQ_BUF_SIZE) { + hd44780_flush_esc_seq(lcd); + } +} + +void hd44780_write(struct hd44780 *lcd, const char *buf, size_t count) +{ + size_t i; + char ch; + + if (lcd->dirty) { + hd44780_clear_display(lcd); + lcd->dirty = false; + } + + for (i = 0; i < count; i++) { + ch = buf[i]; + + if (lcd->is_in_esc_seq) { + hd44780_handle_esc_seq_char(lcd, ch); + } else { + switch (ch) { + case '\r': + hd44780_handle_carriage_return(lcd); + break; + case '\n': + hd44780_handle_new_line(lcd); + break; + case '\e': + lcd->is_in_esc_seq = true; + break; + default: + hd44780_write_char(lcd, ch); + break; + } + } + } +} +EXPORT_SYMBOL_GPL(hd44780_write); + +void hd44780_print(struct hd44780 *lcd, const char *str) +{ + hd44780_write(lcd, str, strlen(str)); +} +EXPORT_SYMBOL_GPL(hd44780_print); + +void hd44780_set_geometry(struct hd44780 *lcd, struct hd44780_geometry *geo) +{ + lcd->geometry = geo; + + if (lcd->is_in_esc_seq) + hd44780_leave_esc_seq(lcd); + + hd44780_clear_display(lcd); +} +EXPORT_SYMBOL_GPL(hd44780_set_geometry); + +void hd44780_set_backlight(struct hd44780 *lcd, bool backlight) +{ + lcd->backlight = backlight; + /* TODO */ + hd44780_write_instruction(lcd, backlight ? BL : 0x00); +} +EXPORT_SYMBOL_GPL(hd44780_set_backlight); + +static void hd44780_update_display_ctrl(struct hd44780 *lcd) +{ + + hd44780_write_instruction(lcd, HD44780_DISPLAY_CTRL + | HD44780_D_DISPLAY_ON + | (lcd->cursor_display ? HD44780_C_CURSOR_ON + : HD44780_C_CURSOR_OFF) + | (lcd->cursor_blink ? HD44780_B_BLINK_ON + : HD44780_B_BLINK_OFF)); +} + +void hd44780_set_cursor_blink(struct hd44780 *lcd, bool cursor_blink) +{ + lcd->cursor_blink = cursor_blink; + hd44780_update_display_ctrl(lcd); +} +EXPORT_SYMBOL_GPL(hd44780_set_cursor_blink); + +void hd44780_set_cursor_display(struct hd44780 *lcd, bool cursor_display) +{ + lcd->cursor_display= cursor_display; + hd44780_update_display_ctrl(lcd); +} +EXPORT_SYMBOL_GPL(hd44780_set_cursor_display); + +void hd44780_init_lcd(struct hd44780 *lcd) +{ + /* HD44780 requires writing three times to initialize or reset + * according to the hardware errata on page 45 figure 23 of + * the Hitachi HD44780 datasheet */ + hd44780_write_instruction(lcd, HD44780_FUNCTION_SET + | HD44780_DL_8BITS); + mdelay(5); + + hd44780_write_instruction(lcd, HD44780_FUNCTION_SET + | HD44780_DL_8BITS); + udelay(100); + + hd44780_write_instruction(lcd, HD44780_FUNCTION_SET + | HD44780_DL_8BITS); + + hd44780_write_instruction(lcd, HD44780_FUNCTION_SET | HD44780_DL_8BITS + | HD44780_N_2LINES | HD44780_5x10DOTS); + + hd44780_write_instruction(lcd, HD44780_DISPLAY_CTRL | HD44780_D_DISPLAY_ON + | HD44780_C_CURSOR_ON | HD44780_B_BLINK_ON); + udelay(100); + + hd44780_clear_display(lcd); + + hd44780_write_instruction(lcd, HD44780_ENTRY_MODE_SET + | HD44780_ID_INCREMENT | HD44780_S_SHIFT_OFF); +} +EXPORT_SYMBOL_GPL(hd44780_init_lcd); + + +/* Device attributes */ + +static ssize_t geometry_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct hd44780 *lcd; + struct hd44780_geometry *geo; + + lcd = dev_get_drvdata(dev); + geo = lcd->geometry; + + return scnprintf(buf, PAGE_SIZE, "%dx%d\n", geo->cols, geo->rows); +} + +static ssize_t geometry_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct hd44780 *lcd; + struct hd44780_geometry *geo; + int cols = 0, rows = 0, i; + + sscanf(buf, "%dx%d", &cols, &rows); + + for (i = 0; hd44780_geometries[i] != NULL; i++) { + geo = hd44780_geometries[i]; + + if (geo->cols == cols && geo->rows == rows) { + lcd = dev_get_drvdata(dev); + + mutex_lock(&lcd->lock); + hd44780_set_geometry(lcd, geo); + mutex_unlock(&lcd->lock); + + break; + } + } + + return count; +} +static DEVICE_ATTR_RW(geometry); + +static ssize_t backlight_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct hd44780 *lcd = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", lcd->backlight); +} + +static ssize_t backlight_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct hd44780 *lcd = dev_get_drvdata(dev); + + mutex_lock(&lcd->lock); + hd44780_set_backlight(lcd, buf[0] == '1'); + mutex_unlock(&lcd->lock); + + return count; +} +static DEVICE_ATTR_RW(backlight); + +static ssize_t cursor_blink_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct hd44780 *lcd = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", lcd->cursor_blink); +} + +static ssize_t cursor_blink_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct hd44780 *lcd = dev_get_drvdata(dev); + + mutex_lock(&lcd->lock); + hd44780_set_cursor_blink(lcd, buf[0] == '1'); + mutex_unlock(&lcd->lock); + + return count; +} +static DEVICE_ATTR_RW(cursor_blink); + +static ssize_t cursor_display_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct hd44780 *lcd = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", lcd->cursor_display); +} + +static ssize_t cursor_display_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct hd44780 *lcd = dev_get_drvdata(dev); + + mutex_lock(&lcd->lock); + hd44780_set_cursor_display(lcd, buf[0] == '1'); + mutex_unlock(&lcd->lock); + + return count; +} +static DEVICE_ATTR_RW(cursor_display); + +static struct attribute *hd44780_device_attrs[] = { + &dev_attr_geometry.attr, + &dev_attr_backlight.attr, + &dev_attr_cursor_blink.attr, + &dev_attr_cursor_display.attr, + NULL +}; +ATTRIBUTE_GROUPS(hd44780_device); + +/* File operations */ + +static int hd44780_file_open(struct inode *inode, struct file *filp) +{ + filp->private_data = container_of(inode->i_cdev, struct hd44780, cdev); + return 0; +} + +static int hd44780_file_release(struct inode *inode, struct file *filp) +{ + struct hd44780 *lcd = filp->private_data; + hd44780_flush(lcd); + return 0; +} + +static ssize_t hd44780_file_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp) +{ + struct hd44780 *lcd; + size_t n; + + lcd = filp->private_data; + n = min(count, (size_t)BUF_SIZE); + + // TODO: Consider using an interruptible lock + mutex_lock(&lcd->lock); + + // TODO: Support partial writes during errors? + if (copy_from_user(lcd->buf, buf, n)) { + mutex_unlock(&lcd->lock); + return -EFAULT; + } + + hd44780_write(lcd, lcd->buf, n); + + mutex_unlock(&lcd->lock); + + return n; +} + +static void hd44780_init(struct hd44780 *lcd, struct hd44780_geometry *geometry, + struct i2c_client *i2c_client) +{ + lcd->geometry = geometry; + lcd->i2c_client = i2c_client; + lcd->pos.row = 0; + lcd->pos.col = 0; + memset(lcd->esc_seq_buf.buf, 0, ESC_SEQ_BUF_SIZE); + lcd->esc_seq_buf.length = 0; + lcd->is_in_esc_seq = false; + lcd->backlight = true; + lcd->cursor_blink = true; + lcd->cursor_display = true; + mutex_init(&lcd->lock); +} + +static struct file_operations fops = { + .open = hd44780_file_open, + .release = hd44780_file_release, + .write = hd44780_file_write, +}; + +static int hd44780_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + dev_t devt; + struct hd44780 *lcd; + struct device *device; + int ret, minor; + + minor = atomic_inc_return(&next_minor); + devt = MKDEV(MAJOR(dev_no), minor); + + lcd = (struct hd44780 *)kmalloc(sizeof(struct hd44780), GFP_KERNEL); + if (!lcd) { + return -ENOMEM; + } + + /* JHD1802, default resolution 16x2 */ + hd44780_init(lcd, hd44780_geometries[1], client); + + spin_lock(&hd44780_list_lock); + list_add(&lcd->list, &hd44780_list); + spin_unlock(&hd44780_list_lock); + + cdev_init(&lcd->cdev, &fops); + ret = cdev_add(&lcd->cdev, devt, 1); + if (ret) { + pr_warn("Can't add cdev\n"); + goto exit; + } + + device = device_create_with_groups(hd44780_class, NULL, devt, NULL, + hd44780_device_groups, "lcd%d", MINOR(devt)); + + if (IS_ERR(device)) { + ret = PTR_ERR(device); + pr_warn("Can't create device\n"); + goto del_exit; + } + + dev_set_drvdata(device, lcd); + lcd->device = device; + + hd44780_init_lcd(lcd); + + hd44780_print(lcd, "Grove-16x2 LCD\nJHD1802M0"); + lcd->dirty = true; + + return 0; + +del_exit: + cdev_del(&lcd->cdev); + + spin_lock(&hd44780_list_lock); + list_del(&lcd->list); + spin_unlock(&hd44780_list_lock); +exit: + kfree(lcd); + + return ret; +} + +static struct hd44780 * get_hd44780_by_i2c_client(struct i2c_client *i2c_client) +{ + struct hd44780 *lcd; + + spin_lock(&hd44780_list_lock); + list_for_each_entry(lcd, &hd44780_list, list) { + if (lcd->i2c_client->addr == i2c_client->addr) { + spin_unlock(&hd44780_list_lock); + return lcd; + } + } + spin_unlock(&hd44780_list_lock); + + return NULL; +} + + +static int hd44780_remove(struct i2c_client *client) +{ + struct hd44780 *lcd; + lcd = get_hd44780_by_i2c_client(client); + device_destroy(hd44780_class, lcd->device->devt); + cdev_del(&lcd->cdev); + + spin_lock(&hd44780_list_lock); + list_del(&lcd->list); + spin_unlock(&hd44780_list_lock); + + kfree(lcd); + + return 0; +} + +static const struct i2c_device_id hd44780_id[] = { + { NAME, 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, hd44780_id); + +static struct i2c_driver hd44780_driver = { + .driver = { + .name = NAME, + .owner = THIS_MODULE, + }, + .probe = hd44780_probe, + .remove = hd44780_remove, + .id_table = hd44780_id, +}; + +static int __init hd44780_mod_init(void) +{ + int ret; + + ret = alloc_chrdev_region(&dev_no, 0, NUM_DEVICES, NAME); + if (ret) { + pr_warn("Can't allocate chardev region"); + return ret; + } + + hd44780_class = class_create(THIS_MODULE, CLASS_NAME); + if (IS_ERR(hd44780_class)) { + ret = PTR_ERR(hd44780_class); + pr_warn("Can't create %s class\n", CLASS_NAME); + goto exit; + } + + ret = i2c_add_driver(&hd44780_driver); + if (ret) { + pr_warn("Can't register I2C driver %s\n", hd44780_driver.driver.name); + goto destroy_exit; + } + + return 0; + +destroy_exit: + class_destroy(hd44780_class); +exit: + unregister_chrdev_region(dev_no, NUM_DEVICES); + + return ret; +} +module_init(hd44780_mod_init); + +static void __exit hd44780_mod_exit(void) +{ + i2c_del_driver(&hd44780_driver); + class_destroy(hd44780_class); + unregister_chrdev_region(dev_no, NUM_DEVICES); +} +module_exit(hd44780_mod_exit); + +MODULE_AUTHOR("Mariusz Gorski "); +MODULE_AUTHOR("Peter Yang "); +MODULE_DESCRIPTION("JHD1802 HD44780 I2C driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/auxdisplay/seeed-hd44780.h b/drivers/auxdisplay/seeed-hd44780.h new file mode 100644 index 0000000000000..7c3f6ffa15c48 --- /dev/null +++ b/drivers/auxdisplay/seeed-hd44780.h @@ -0,0 +1,52 @@ +#ifndef _SEEED_HD44780_H_ +#define _SEEED_HD44780_H_ + +#define BUF_SIZE 64 +#define ESC_SEQ_BUF_SIZE 4 + +struct hd44780_geometry { + int cols; + int rows; + int start_addrs[]; +}; + +struct hd44780 { + struct cdev cdev; + struct device *device; + struct i2c_client *i2c_client; + struct hd44780_geometry *geometry; + + /* Current cursor positon on the display */ + struct { + int row; + int col; + } pos; + + char buf[BUF_SIZE]; + struct { + char buf[ESC_SEQ_BUF_SIZE]; + int length; + } esc_seq_buf; + bool is_in_esc_seq; + + bool backlight; + bool cursor_blink; + bool cursor_display; + + bool dirty; + + struct mutex lock; + struct list_head list; +}; + +void hd44780_write(struct hd44780 *, const char *, size_t); +void hd44780_init_lcd(struct hd44780 *); +void hd44780_print(struct hd44780 *, const char *); +void hd44780_flush(struct hd44780 *); +void hd44780_set_geometry(struct hd44780 *, struct hd44780_geometry *); +void hd44780_set_backlight(struct hd44780 *, bool); +void hd44780_set_cursor_blink(struct hd44780 *, bool); +void hd44780_set_cursor_display(struct hd44780 *, bool); + +extern struct hd44780_geometry *hd44780_geometries[]; +#endif From 1de74c1255a6f94ffed37ef4504e87cdb3838414 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Tue, 28 Sep 2021 11:33:53 -0500 Subject: [PATCH 51/65] hwmon/sht3x.c: dt support Signed-off-by: Robert Nelson --- drivers/hwmon/sht3x.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/sht3x.c b/drivers/hwmon/sht3x.c index 3f279aa1cee5e..60f8971a16f5b 100644 --- a/drivers/hwmon/sht3x.c +++ b/drivers/hwmon/sht3x.c @@ -738,9 +738,16 @@ static const struct i2c_device_id sht3x_ids[] = { {"sts3x", sts3x}, {} }; - MODULE_DEVICE_TABLE(i2c, sht3x_ids); +static const struct of_device_id shtc1_of_match[] = { + { .compatible = "sensirion,shtc1" }, + { .compatible = "sensirion,shtw1" }, + { .compatible = "sensirion,shtc3" }, + { } +}; +MODULE_DEVICE_TABLE(of, shtc1_of_match); + static struct i2c_driver sht3x_i2c_driver = { .driver.name = "sht3x", .probe_new = sht3x_probe, From 651e56231fd7af83dedb04d1b72250f68605efc1 Mon Sep 17 00:00:00 2001 From: Paul Barker Date: Mon, 18 Oct 2021 12:20:13 +0100 Subject: [PATCH 52/65] qcacld support: Add flag WIPHY_FLAG_DFS_OFFLOAD Taken from commit 8e407b62f2ed89fe41c40a976669df1693cf7027 in https://github.com/boundarydevices/linux-imx6. cfg80211: Using new wiphy flag WIPHY_FLAG_DFS_OFFLOAD When flag WIPHY_FLAG_DFS_OFFLOAD is defined, the driver would handle all the DFS related operations. Therefore the kernel needs to ignore the DFS state that it uses to block the userspace calls to the driver through cfg8021 APIs. Also it should treat the userspace calls to start radar detection as a no-op. Ref: BSP-65 Signed-off-by: Paul Barker --- include/net/cfg80211.h | 1 + net/wireless/chan.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index fe3a4f77bfae2..9fb231850e2f2 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4326,6 +4326,7 @@ enum wiphy_flags { WIPHY_FLAG_SUPPORTS_5_10_MHZ = BIT(22), WIPHY_FLAG_HAS_CHANNEL_SWITCH = BIT(23), WIPHY_FLAG_HAS_STATIC_WEP = BIT(24), + WIPHY_FLAG_DFS_OFFLOAD = BIT(25) }; /** diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 22d1779ab2b1b..b7c4c8ad77d36 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -874,7 +874,8 @@ static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) { c = ieee80211_get_channel_khz(wiphy, freq); - if (!c || c->flags & prohibited_flags) + if (!c || ((c->flags & prohibited_flags) && + !(wiphy->flags & WIPHY_FLAG_DFS_OFFLOAD))) return false; } From f88fc144f5e302ad4aed55593433c910aecb7b74 Mon Sep 17 00:00:00 2001 From: Paul Barker Date: Mon, 18 Oct 2021 12:25:16 +0100 Subject: [PATCH 53/65] qcacld support: Export regulatory_hint_user() Taken from commit 8e407b62f2ed89fe41c40a976669df1693cf7027 in https://github.com/boundarydevices/linux-imx6. Ref: BSP-65 Signed-off-by: Paul Barker --- include/net/cfg80211.h | 26 ++++++++++++++++++++++++++ net/wireless/reg.c | 1 + 2 files changed, 27 insertions(+) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 9fb231850e2f2..e60bd4d6f0f8e 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5909,6 +5909,32 @@ void cfg80211_send_layer2_update(struct net_device *dev, const u8 *addr); */ int regulatory_hint(struct wiphy *wiphy, const char *alpha2); +/** + * regulatory_hint_user - hint to the wireless core a regulatory domain + * which the driver has received from an application + * @alpha2: the ISO/IEC 3166 alpha2 the driver claims its regulatory domain + * should be in. If @rd is set this should be NULL. Note that if you + * set this to NULL you should still set rd->alpha2 to some accepted + * alpha2. + * @user_reg_hint_type: the type of user regulatory hint. + * + * Wireless drivers can use this function to hint to the wireless core + * the current regulatory domain as specified by trusted applications, + * it is the driver's responsibilty to estbalish which applications it + * trusts. + * + * The wiphy should be registered to cfg80211 prior to this call. + * For cfg80211 drivers this means you must first use wiphy_register(), + * for mac80211 drivers you must first use ieee80211_register_hw(). + * + * Drivers should check the return value, its possible you can get + * an -ENOMEM or an -EINVAL. + * + * Return: 0 on success. -ENOMEM, -EINVAL. + */ +int regulatory_hint_user(const char *alpha2, + enum nl80211_user_reg_hint_type user_reg_hint_type); + /** * regulatory_set_wiphy_regd - set regdom info for self managed drivers * @wiphy: the wireless device we want to process the regulatory domain on diff --git a/net/wireless/reg.c b/net/wireless/reg.c index a04fdfb35f070..4ec31b36e8991 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -3171,6 +3171,7 @@ int regulatory_hint_user(const char *alpha2, return 0; } +EXPORT_SYMBOL(regulatory_hint_user); int regulatory_hint_indoor(bool is_indoor, u32 portid) { From b25db59e153df613ae09467fd6fa1c5959e631ef Mon Sep 17 00:00:00 2001 From: Paul Barker Date: Mon, 18 Oct 2021 12:26:21 +0100 Subject: [PATCH 54/65] qcacld support: Add cfg80211_is_gratuitous_arp_unsolicited_na() Taken from commit 8e407b62f2ed89fe41c40a976669df1693cf7027 in https://github.com/boundarydevices/linux-imx6. Ref: BSP-65 Signed-off-by: Paul Barker --- include/net/cfg80211.h | 9 ++++++++ net/wireless/util.c | 51 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index e60bd4d6f0f8e..6e2439b16149d 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -7993,6 +7993,15 @@ void cfg80211_pmsr_complete(struct wireless_dev *wdev, bool cfg80211_iftype_allowed(struct wiphy *wiphy, enum nl80211_iftype iftype, bool is_4addr, u8 check_swif); +/** + * cfg80211_is_gratuitous_arp_unsolicited_na - packet is grat. ARP/unsol. NA + * @skb: the input packet, must be an ethernet frame already + * + * Return: %true if the packet is a gratuitous ARP or unsolicited NA packet. + * This is used to drop packets that shouldn't occur because the AP implements + * a proxy service. + */ +bool cfg80211_is_gratuitous_arp_unsolicited_na(struct sk_buff *skb); /* Logging, debugging and troubleshooting/diagnostic helpers. */ diff --git a/net/wireless/util.c b/net/wireless/util.c index e4247c3543566..29b1b042bf446 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -2244,3 +2244,54 @@ bool cfg80211_iftype_allowed(struct wiphy *wiphy, enum nl80211_iftype iftype, return false; } EXPORT_SYMBOL(cfg80211_iftype_allowed); + +bool cfg80211_is_gratuitous_arp_unsolicited_na(struct sk_buff *skb) +{ + const struct ethhdr *eth = (void *)skb->data; + const struct { + struct arphdr hdr; + u8 ar_sha[ETH_ALEN]; + u8 ar_sip[4]; + u8 ar_tha[ETH_ALEN]; + u8 ar_tip[4]; + } __packed *arp; + const struct ipv6hdr *ipv6; + const struct icmp6hdr *icmpv6; + + switch (eth->h_proto) { + case cpu_to_be16(ETH_P_ARP): + /* can't say - but will probably be dropped later anyway */ + if (!pskb_may_pull(skb, sizeof(*eth) + sizeof(*arp))) + return false; + + arp = (void *)(eth + 1); + + if ((arp->hdr.ar_op == cpu_to_be16(ARPOP_REPLY) || + arp->hdr.ar_op == cpu_to_be16(ARPOP_REQUEST)) && + !memcmp(arp->ar_sip, arp->ar_tip, sizeof(arp->ar_sip))) + return true; + break; + case cpu_to_be16(ETH_P_IPV6): + /* can't say - but will probably be dropped later anyway */ + if (!pskb_may_pull(skb, sizeof(*eth) + sizeof(*ipv6) + + sizeof(*icmpv6))) + return false; + + ipv6 = (void *)(eth + 1); + icmpv6 = (void *)(ipv6 + 1); + + if (icmpv6->icmp6_type == NDISC_NEIGHBOUR_ADVERTISEMENT && + !memcmp(&ipv6->saddr, &ipv6->daddr, sizeof(ipv6->saddr))) + return true; + break; + default: + /* + * no need to support other protocols, proxy service isn't + * specified for any others + */ + break; + } + + return false; +} +EXPORT_SYMBOL(cfg80211_is_gratuitous_arp_unsolicited_na); From 4ce6842e429e24fba64fd51686c1bb18dee91368 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Thu, 19 Mar 2020 14:00:05 -0500 Subject: [PATCH 55/65] bootup-hacks: xor select neon or arm4regs already default on BBB/X15, just calculated on every bootup Signed-off-by: Robert Nelson --- arch/arm/include/asm/xor.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm/include/asm/xor.h b/arch/arm/include/asm/xor.h index aefddec79286a..4f0732aa787e3 100644 --- a/arch/arm/include/asm/xor.h +++ b/arch/arm/include/asm/xor.h @@ -209,3 +209,9 @@ static struct xor_block_template xor_block_neon = { #else #define NEON_TEMPLATES #endif + +#ifdef CONFIG_KERNEL_MODE_NEON +#define XOR_SELECT_TEMPLATE(FASTEST) (&xor_block_neon) +#else +#define XOR_SELECT_TEMPLATE(FASTEST) (&xor_block_arm4regs) +#endif From e5522eafb06f4c8d5e0d9b8f5f40a9b20f1360d3 Mon Sep 17 00:00:00 2001 From: Matthijs van Duin Date: Sat, 9 Feb 2019 17:44:46 +0100 Subject: [PATCH 56/65] uio-pruss: cleanups and pruss v2 (pru-icss) support --- drivers/uio/uio_pruss.c | 124 +++++++++++++++++++++++++++++----------- 1 file changed, 90 insertions(+), 34 deletions(-) diff --git a/drivers/uio/uio_pruss.c b/drivers/uio/uio_pruss.c index 1cc175d3c25c5..dabc37ed64594 100644 --- a/drivers/uio/uio_pruss.c +++ b/drivers/uio/uio_pruss.c @@ -26,14 +26,22 @@ #include #include #include +#include +#ifdef CONFIG_ARCH_DAVINCI_DA850 #include +#else +#include +#endif #define DRV_NAME "pruss_uio" #define DRV_VERSION "1.0" +#ifdef CONFIG_ARCH_DAVINCI_DA850 static int sram_pool_sz = SZ_16K; module_param(sram_pool_sz, int, 0); -MODULE_PARM_DESC(sram_pool_sz, "sram pool size to allocate "); +MODULE_PARM_DESC(sram_pool_sz, "sram pool size to allocate"); +#endif + static int extram_pool_sz = SZ_256K; module_param(extram_pool_sz, int, 0); @@ -61,22 +69,23 @@ MODULE_PARM_DESC(extram_pool_sz, "external ram pool size to allocate"); #define PINTC_HIER 0x1500 struct uio_pruss_dev { - struct uio_info *info; - struct clk *pruss_clk; - dma_addr_t sram_paddr; + struct uio_info info[MAX_PRUSS_EVT]; dma_addr_t ddr_paddr; void __iomem *prussio_vaddr; - unsigned long sram_vaddr; void *ddr_vaddr; - unsigned int hostirq_start; unsigned int pintc_base; +#ifdef CONFIG_ARCH_DAVINCI_DA850 + struct clk *pruss_clk; struct gen_pool *sram_pool; + dma_addr_t sram_paddr; + unsigned long sram_vaddr; +#endif }; static irqreturn_t pruss_handler(int irq, struct uio_info *info) { struct uio_pruss_dev *gdev = info->priv; - int intr_bit = (irq - gdev->hostirq_start + 2); + int intr_bit = 2 + (info - gdev->info); int val, intr_mask = (1 << intr_bit); void __iomem *base = gdev->prussio_vaddr + gdev->pintc_base; void __iomem *intren_reg = base + PINTC_HIER; @@ -94,53 +103,50 @@ static irqreturn_t pruss_handler(int irq, struct uio_info *info) static void pruss_cleanup(struct device *dev, struct uio_pruss_dev *gdev) { - int cnt; - struct uio_info *p = gdev->info; + int i; - for (cnt = 0; cnt < MAX_PRUSS_EVT; cnt++, p++) { - uio_unregister_device(p); - kfree(p->name); + for (i = 0; i < MAX_PRUSS_EVT; i++) { + uio_unregister_device(&gdev->info[i]); + kfree(gdev->info[i].name); } iounmap(gdev->prussio_vaddr); if (gdev->ddr_vaddr) { dma_free_coherent(dev, extram_pool_sz, gdev->ddr_vaddr, gdev->ddr_paddr); } +#ifdef CONFIG_ARCH_DAVINCI_DA850 if (gdev->sram_vaddr) gen_pool_free(gdev->sram_pool, gdev->sram_vaddr, sram_pool_sz); - kfree(gdev->info); clk_disable(gdev->pruss_clk); clk_put(gdev->pruss_clk); +#else + pm_runtime_put(dev); + pm_runtime_disable(dev); +#endif kfree(gdev); } static int pruss_probe(struct platform_device *pdev) { - struct uio_info *p; struct uio_pruss_dev *gdev; struct resource *regs_prussio; struct device *dev = &pdev->dev; - int ret, cnt, i, len; + int ret, i, len; struct uio_pruss_pdata *pdata = dev_get_platdata(dev); gdev = kzalloc(sizeof(struct uio_pruss_dev), GFP_KERNEL); if (!gdev) return -ENOMEM; - gdev->info = kcalloc(MAX_PRUSS_EVT, sizeof(*p), GFP_KERNEL); - if (!gdev->info) { - ret = -ENOMEM; - goto err_free_gdev; - } - +#ifdef CONFIG_ARCH_DAVINCI_DA850 /* Power on PRU in case its not done as part of boot-loader */ gdev->pruss_clk = clk_get(dev, "pruss"); if (IS_ERR(gdev->pruss_clk)) { dev_err(dev, "Failed to get clock\n"); ret = PTR_ERR(gdev->pruss_clk); - goto err_free_info; + goto err_free_gdev; } ret = clk_enable(gdev->pruss_clk); @@ -148,6 +154,15 @@ static int pruss_probe(struct platform_device *pdev) dev_err(dev, "Failed to enable clock\n"); goto err_clk_put; } +#else + pm_runtime_enable(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "pm_runtime_get_sync() failed\n"); + pm_runtime_disable(dev); + goto err_free_gdev; + } +#endif regs_prussio = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!regs_prussio) { @@ -162,6 +177,18 @@ static int pruss_probe(struct platform_device *pdev) goto err_clk_disable; } + if (dev->of_node) { + ret = of_property_read_u32(dev->of_node, + "ti,pintc-offset", + &gdev->pintc_base); + if (ret < 0) { + dev_err(dev, "Can't parse ti,pintc-offset property\n"); + goto err_clk_disable; + } + } else + gdev->pintc_base = pdata->pintc_base; + +#ifdef CONFIG_ARCH_DAVINCI_DA850 if (pdata->sram_pool) { gdev->sram_pool = pdata->sram_pool; gdev->sram_vaddr = @@ -173,9 +200,10 @@ static int pruss_probe(struct platform_device *pdev) goto err_clk_disable; } } +#endif gdev->ddr_vaddr = dma_alloc_coherent(dev, extram_pool_sz, - &(gdev->ddr_paddr), GFP_KERNEL | GFP_DMA); + &gdev->ddr_paddr, GFP_KERNEL | GFP_DMA); if (!gdev->ddr_vaddr) { dev_err(dev, "Could not allocate external memory\n"); ret = -ENOMEM; @@ -185,32 +213,48 @@ static int pruss_probe(struct platform_device *pdev) len = resource_size(regs_prussio); gdev->prussio_vaddr = ioremap(regs_prussio->start, len); if (!gdev->prussio_vaddr) { - dev_err(dev, "Can't remap PRUSS I/O address range\n"); + dev_err(dev, "Can't remap PRUSS I/O address range\n"); ret = -ENOMEM; goto err_free_ddr_vaddr; } - gdev->pintc_base = pdata->pintc_base; - gdev->hostirq_start = platform_get_irq(pdev, 0); + for (i = 0; i < MAX_PRUSS_EVT; i++) { + struct uio_info *p = &gdev->info[i]; - for (cnt = 0, p = gdev->info; cnt < MAX_PRUSS_EVT; cnt++, p++) { + p->mem[0].name = "pruss"; p->mem[0].addr = regs_prussio->start; p->mem[0].size = resource_size(regs_prussio); p->mem[0].memtype = UIO_MEM_PHYS; + /* note: some userspace code uses hardcoded mem indices... */ +#ifdef CONFIG_ARCH_DAVINCI_DA850 + p->mem[1].name = "sram"; p->mem[1].addr = gdev->sram_paddr; p->mem[1].size = sram_pool_sz; p->mem[1].memtype = UIO_MEM_PHYS; + p->mem[2].name = "ddr"; p->mem[2].addr = gdev->ddr_paddr; p->mem[2].size = extram_pool_sz; p->mem[2].memtype = UIO_MEM_PHYS; +#else + p->mem[1].name = "ddr"; + p->mem[1].addr = gdev->ddr_paddr; + p->mem[1].size = extram_pool_sz; + p->mem[1].memtype = UIO_MEM_PHYS; +#endif - p->name = kasprintf(GFP_KERNEL, "pruss_evt%d", cnt); + ret = platform_get_irq(pdev, i); + if (ret < 0) { + dev_err(dev, "Failed to obtain irq %d (%d)\n", i, ret); + goto err_unloop; + } + + p->name = kasprintf(GFP_KERNEL, "pruss_evt%d", i); p->version = DRV_VERSION; /* Register PRUSS IRQ lines */ - p->irq = gdev->hostirq_start + cnt; + p->irq = ret; p->handler = pruss_handler; p->priv = gdev; @@ -225,23 +269,27 @@ static int pruss_probe(struct platform_device *pdev) return 0; err_unloop: - for (i = 0, p = gdev->info; i < cnt; i++, p++) { - uio_unregister_device(p); - kfree(p->name); + while( --i >= 0 ) { + uio_unregister_device(&gdev->info[i]); + kfree(gdev->info[i].name); } iounmap(gdev->prussio_vaddr); err_free_ddr_vaddr: dma_free_coherent(dev, extram_pool_sz, gdev->ddr_vaddr, gdev->ddr_paddr); err_free_sram: +#ifdef CONFIG_ARCH_DAVINCI_DA850 if (pdata->sram_pool) gen_pool_free(gdev->sram_pool, gdev->sram_vaddr, sram_pool_sz); err_clk_disable: clk_disable(gdev->pruss_clk); err_clk_put: clk_put(gdev->pruss_clk); -err_free_info: - kfree(gdev->info); +#else +err_clk_disable: + pm_runtime_put(dev); + pm_runtime_disable(dev); +#endif err_free_gdev: kfree(gdev); @@ -256,11 +304,19 @@ static int pruss_remove(struct platform_device *dev) return 0; } +static const struct of_device_id pruss_dt_ids[] = { + { .compatible = "ti,pruss-v1" }, + { .compatible = "ti,pruss-v2" }, + {}, +}; +MODULE_DEVICE_TABLE(of, pruss_dt_ids); + static struct platform_driver pruss_driver = { .probe = pruss_probe, .remove = pruss_remove, .driver = { .name = DRV_NAME, + .of_match_table = pruss_dt_ids, }, }; From 5f8213545dd271301d7db26f754243fb731ba07f Mon Sep 17 00:00:00 2001 From: Matthijs van Duin Date: Wed, 16 Mar 2022 16:47:13 +0100 Subject: [PATCH 57/65] add compatible-strings to uio_pdrv_genirq "uio" for generic use "ti,pruss-shmem" for backwards compatibility the of_id module parameter is still supported to add another id --- drivers/uio/uio_pdrv_genirq.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c index 63258b6accc46..247a067aa483c 100644 --- a/drivers/uio/uio_pdrv_genirq.c +++ b/drivers/uio/uio_pdrv_genirq.c @@ -276,11 +276,13 @@ static const struct dev_pm_ops uio_pdrv_genirq_dev_pm_ops = { #ifdef CONFIG_OF static struct of_device_id uio_of_genirq_match[] = { + { .compatible = "uio" }, + { .compatible = "ti,pruss-shmem" }, { /* This is filled with module_parm */ }, { /* Sentinel */ }, }; MODULE_DEVICE_TABLE(of, uio_of_genirq_match); -module_param_string(of_id, uio_of_genirq_match[0].compatible, 128, 0); +module_param_string(of_id, uio_of_genirq_match[2].compatible, 128, 0); MODULE_PARM_DESC(of_id, "Openfirmware id of the device to be handled by uio"); #endif From 18b017016d43d2213f69acbb0a322aadc4c106dc Mon Sep 17 00:00:00 2001 From: Jason Kridner Date: Tue, 18 Jan 2022 23:06:21 -0500 Subject: [PATCH 58/65] bb.org: Boris on Tux --- drivers/video/logo/Kconfig | 4 + drivers/video/logo/Makefile | 1 + drivers/video/logo/logo.c | 4 + drivers/video/logo/logo_beagle_clut224.ppm | 1123 ++++++++++++++++++++ include/linux/linux_logo.h | 1 + 5 files changed, 1133 insertions(+) create mode 100644 drivers/video/logo/logo_beagle_clut224.ppm diff --git a/drivers/video/logo/Kconfig b/drivers/video/logo/Kconfig index 6d6f8c08792dc..ca7dde5c8298c 100644 --- a/drivers/video/logo/Kconfig +++ b/drivers/video/logo/Kconfig @@ -28,6 +28,10 @@ config LOGO_LINUX_CLUT224 bool "Standard 224-color Linux logo" default y +config LOGO_BEAGLE_CLUT224 + bool "224-color Linux logo with BeagleBoard.org mascot Boris" + default y + config LOGO_DEC_CLUT224 bool "224-color Digital Equipment Corporation Linux logo" depends on MACH_DECSTATION || ALPHA diff --git a/drivers/video/logo/Makefile b/drivers/video/logo/Makefile index 895c60b8402ea..cb7233c192be2 100644 --- a/drivers/video/logo/Makefile +++ b/drivers/video/logo/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_LOGO) += logo.o obj-$(CONFIG_LOGO_LINUX_MONO) += logo_linux_mono.o obj-$(CONFIG_LOGO_LINUX_VGA16) += logo_linux_vga16.o obj-$(CONFIG_LOGO_LINUX_CLUT224) += logo_linux_clut224.o +obj-$(CONFIG_LOGO_BEAGLE_CLUT224) += logo_beagle_clut224.o obj-$(CONFIG_LOGO_DEC_CLUT224) += logo_dec_clut224.o obj-$(CONFIG_LOGO_MAC_CLUT224) += logo_mac_clut224.o obj-$(CONFIG_LOGO_PARISC_CLUT224) += logo_parisc_clut224.o diff --git a/drivers/video/logo/logo.c b/drivers/video/logo/logo.c index 141f15a9a4590..19d537b060fac 100644 --- a/drivers/video/logo/logo.c +++ b/drivers/video/logo/logo.c @@ -99,6 +99,10 @@ const struct linux_logo * __ref fb_find_logo(int depth) #ifdef CONFIG_LOGO_SUPERH_CLUT224 /* SuperH Linux logo */ logo = &logo_superh_clut224; +#endif +#ifdef CONFIG_LOGO_BEAGLE_CLUT224 + /* Generic Linux logo */ + logo = &logo_beagle_clut224; #endif } return logo; diff --git a/drivers/video/logo/logo_beagle_clut224.ppm b/drivers/video/logo/logo_beagle_clut224.ppm new file mode 100644 index 0000000000000..dd0f7bf9997eb --- /dev/null +++ b/drivers/video/logo/logo_beagle_clut224.ppm @@ -0,0 +1,1123 @@ +P3 +80 80 +255 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +6 6 6 6 6 6 10 10 10 10 10 10 10 10 10 6 6 6 +6 6 6 6 6 6 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 6 6 6 10 10 10 14 14 14 +22 22 22 26 26 26 30 30 30 34 34 34 30 30 30 30 30 30 +26 26 26 18 18 18 14 14 14 10 10 10 6 6 6 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 +0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 6 6 6 14 14 14 26 26 26 42 42 42 +54 54 54 66 66 66 78 78 78 78 78 78 78 78 78 74 74 74 +66 66 66 54 54 54 42 42 42 26 26 26 18 18 18 10 10 10 +6 6 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 10 10 10 22 22 22 42 42 42 66 66 66 86 86 86 +66 66 66 38 38 38 38 38 38 22 22 22 26 26 26 34 34 34 +54 54 54 66 66 66 86 86 86 70 70 70 46 46 46 26 26 26 +14 14 14 6 6 6 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 +0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +10 10 10 26 26 26 50 50 50 82 82 82 58 58 58 6 6 6 +2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 2 2 6 6 6 6 54 54 54 86 86 86 66 66 66 +38 38 38 18 18 18 6 6 6 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 6 6 +22 22 22 50 50 50 78 78 78 34 34 34 2 2 6 2 2 6 +2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 2 2 6 2 2 6 2 2 6 6 6 6 70 70 70 +78 78 78 46 46 46 22 22 22 6 6 6 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 +0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 6 6 6 18 18 18 +42 42 42 82 82 82 26 26 26 2 2 6 2 2 6 2 2 6 +2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 14 14 14 46 46 46 34 34 34 6 6 6 2 2 6 +42 42 42 78 78 78 42 42 42 18 18 18 6 6 6 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 +0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 10 10 10 30 30 30 +66 66 66 58 58 58 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 26 26 26 86 86 86 101 101 101 46 46 46 10 10 10 +2 2 6 58 58 58 70 70 70 34 34 34 10 10 10 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 +0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 14 14 14 42 42 42 +86 86 86 10 10 10 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 30 30 30 94 94 94 94 94 94 58 58 58 26 26 26 +2 2 6 6 6 6 78 78 78 54 54 54 22 22 22 6 6 6 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 6 6 6 22 22 22 62 62 62 +62 62 62 2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 26 26 26 54 54 54 38 38 38 18 18 18 10 10 10 +2 2 6 2 2 6 34 34 34 82 82 82 38 38 38 14 14 14 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 +0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 6 6 6 30 30 30 78 78 78 +30 30 30 2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 10 10 10 10 10 10 2 2 6 2 2 6 2 2 6 +2 2 6 2 2 6 2 2 6 78 78 78 50 50 50 18 18 18 +6 6 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 10 10 10 38 38 38 86 86 86 +14 14 14 2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 2 2 6 2 2 6 54 54 54 66 66 66 26 26 26 +6 6 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 +0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 14 14 14 42 42 42 82 82 82 +2 2 6 2 2 6 2 2 6 6 6 6 10 10 10 2 2 6 +2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 6 6 6 14 14 14 10 10 10 2 2 6 2 2 6 +2 2 6 2 2 6 2 2 6 18 18 18 82 82 82 34 34 34 +10 10 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 14 14 14 46 46 46 86 86 86 +2 2 6 2 2 6 6 6 6 6 6 6 22 22 22 34 34 34 +6 6 6 2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 +18 18 18 34 34 34 10 10 10 50 50 50 22 22 22 2 2 6 +2 2 6 2 2 6 2 2 6 10 10 10 86 86 86 42 42 42 +14 14 14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 +0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 14 14 14 46 46 46 86 86 86 +2 2 6 2 2 6 38 38 38 116 116 116 94 94 94 22 22 22 +22 22 22 2 2 6 2 2 6 2 2 6 14 14 14 86 86 86 +138 138 138 162 162 162 158 155 152 38 38 38 26 26 26 6 6 6 +2 2 6 2 2 6 2 2 6 2 2 6 86 86 86 46 46 46 +14 14 14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 14 14 14 46 46 46 86 86 86 +2 2 6 14 14 14 138 138 138 198 198 198 195 195 195 116 116 116 +10 10 10 2 2 6 2 2 6 6 6 6 101 98 89 187 187 187 +210 210 210 218 218 218 218 218 218 138 138 138 14 14 14 6 6 6 +2 2 6 2 2 6 2 2 6 2 2 6 86 86 86 50 50 50 +18 18 18 6 6 6 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 1 +0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 14 14 14 46 46 46 86 86 86 +2 2 6 54 54 54 218 218 218 195 195 195 226 226 226 246 246 246 +58 58 58 2 2 6 2 2 6 30 30 30 210 210 210 253 253 253 +171 170 170 123 123 123 221 221 221 234 234 234 74 74 74 2 2 6 +2 2 6 2 2 6 2 2 6 2 2 6 70 70 70 58 58 58 +22 22 22 6 6 6 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 14 14 14 46 46 46 82 82 82 +2 2 6 106 106 106 171 170 170 26 26 26 86 86 86 226 226 226 +123 123 123 10 10 10 14 14 14 46 46 46 231 231 231 190 190 190 +6 6 6 70 70 70 90 90 90 238 238 238 158 158 158 2 2 6 +2 2 6 2 2 6 2 2 6 2 2 6 70 70 70 58 58 58 +22 22 22 6 6 6 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 1 +0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 14 14 14 42 42 42 86 86 86 +6 6 6 116 116 116 106 106 106 6 6 6 70 70 70 144 144 144 +128 128 128 18 18 18 38 38 38 54 54 54 221 221 221 106 106 106 +2 2 6 14 14 14 46 46 46 190 190 190 198 198 198 2 2 6 +2 2 6 2 2 6 2 2 6 2 2 6 74 74 74 62 62 62 +22 22 22 6 6 6 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 +0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 14 14 14 42 42 42 94 94 94 +14 14 14 101 101 101 128 128 128 2 2 6 18 18 18 116 116 116 +118 98 46 121 92 8 121 92 8 98 77 21 162 162 162 106 106 106 +2 2 6 2 2 6 2 2 6 195 195 195 195 195 195 6 6 6 +2 2 6 2 2 6 2 2 6 2 2 6 74 74 74 62 62 62 +22 22 22 6 6 6 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 1 0 0 1 0 0 1 0 0 0 +0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 10 10 10 38 38 38 90 90 90 +14 14 14 58 58 58 210 210 210 26 26 26 61 42 6 156 107 11 +226 170 11 236 186 11 225 175 15 184 144 12 225 175 15 175 146 61 +37 26 9 2 2 6 70 70 70 246 246 246 138 138 138 2 2 6 +2 2 6 2 2 6 2 2 6 2 2 6 70 70 70 66 66 66 +26 26 26 6 6 6 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 10 10 10 38 38 38 86 86 86 +14 14 14 10 10 10 195 195 195 188 164 115 192 133 9 225 175 15 +239 182 13 236 186 11 232 195 16 232 195 16 246 215 20 241 208 19 +232 195 16 184 144 12 218 194 134 219 214 185 42 42 42 2 2 6 +2 2 6 2 2 6 2 2 6 2 2 6 50 50 50 74 74 74 +30 30 30 6 6 6 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 10 10 10 34 34 34 86 86 86 +14 14 14 2 2 6 121 87 25 192 133 9 219 162 10 239 182 13 +236 186 11 232 195 16 241 208 19 246 215 20 246 215 20 246 215 20 +246 215 20 241 208 19 241 208 19 226 184 13 121 87 25 2 2 6 +2 2 6 2 2 6 2 2 6 2 2 6 50 50 50 82 82 82 +34 34 34 10 10 10 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 10 10 10 34 34 34 82 82 82 +30 30 30 61 42 6 180 123 7 206 145 10 230 174 11 239 182 13 +236 186 11 238 202 15 241 208 19 235 218 115 246 215 20 246 215 20 +246 215 20 246 215 20 226 184 13 225 175 15 184 144 12 6 6 6 +2 2 6 2 2 6 2 2 6 2 2 6 26 26 26 94 94 94 +42 42 42 14 14 14 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 10 10 10 30 30 30 78 78 78 +50 50 50 104 69 6 192 133 9 216 158 10 236 178 12 236 186 11 +232 195 16 241 208 19 246 215 20 246 215 20 246 215 20 246 215 20 +241 208 19 200 144 11 200 144 11 216 158 10 163 110 8 2 2 6 +2 2 6 2 2 6 2 2 6 2 2 6 6 6 6 90 90 90 +54 54 54 18 18 18 6 6 6 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 10 10 10 30 30 30 78 78 78 +46 46 46 22 22 22 137 92 6 210 162 10 239 182 13 236 186 11 +238 202 15 241 208 19 246 215 20 246 215 20 241 208 19 210 162 10 +185 133 11 210 150 10 216 158 10 210 150 10 104 69 6 2 2 6 +6 6 6 54 54 54 14 14 14 2 2 6 2 2 6 62 62 62 +74 74 74 30 30 30 10 10 10 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 10 10 10 34 34 34 78 78 78 +50 50 50 6 6 6 92 69 23 138 101 21 190 146 13 226 184 13 +232 195 16 232 195 16 225 175 15 190 146 13 167 114 7 192 133 9 +210 150 10 213 154 11 190 142 34 188 164 115 101 98 89 2 2 6 +2 2 6 78 78 78 116 116 116 58 58 58 2 2 6 22 22 22 +90 90 90 46 46 46 18 18 18 6 6 6 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 10 10 10 38 38 38 86 86 86 +50 50 50 6 6 6 128 128 128 172 154 116 156 107 11 167 114 7 +200 144 11 184 144 12 197 138 11 200 144 11 206 145 10 206 145 10 +197 138 11 188 164 115 195 195 195 198 198 198 171 170 170 14 14 14 +2 2 6 22 22 22 116 116 116 116 116 116 22 22 22 2 2 6 +74 74 74 70 70 70 30 30 30 10 10 10 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 6 6 6 18 18 18 50 50 50 101 101 101 +26 26 26 10 10 10 138 138 138 190 190 190 172 154 116 156 107 11 +197 138 11 200 144 11 197 138 11 192 133 9 180 123 7 190 142 34 +181 170 141 187 187 187 202 202 202 221 221 221 218 218 218 66 66 66 +2 2 6 2 2 6 50 50 50 62 62 62 6 6 6 2 2 6 +10 10 10 90 90 90 50 50 50 18 18 18 6 6 6 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 10 10 10 34 34 34 74 74 74 74 74 74 +2 2 6 6 6 6 144 144 144 198 198 198 190 190 190 181 170 141 +150 121 62 156 107 11 156 107 11 166 123 43 172 154 116 187 187 187 +190 190 190 210 210 210 246 246 246 253 253 253 253 253 253 182 182 182 +6 6 6 2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 62 62 62 74 74 74 34 34 34 14 14 14 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 10 10 10 22 22 22 54 54 54 94 94 94 18 18 18 +2 2 6 46 46 46 234 234 234 221 221 221 190 190 190 190 190 190 +190 190 190 187 187 187 187 187 187 190 190 190 190 190 190 195 195 195 +218 218 218 242 242 242 253 253 253 253 253 253 253 253 253 253 253 253 +82 82 82 2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 14 14 14 86 86 86 54 54 54 22 22 22 6 6 6 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +6 6 6 18 18 18 46 46 46 90 90 90 46 46 46 18 18 18 +6 6 6 182 182 182 253 253 253 246 246 246 206 206 206 190 190 190 +190 190 190 190 190 190 190 190 190 190 190 190 206 206 206 231 231 231 +250 250 250 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +202 202 202 14 14 14 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 2 2 6 42 42 42 86 86 86 42 42 42 18 18 18 +6 6 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 6 6 +14 14 14 38 38 38 74 74 74 66 66 66 2 2 6 6 6 6 +90 90 90 250 250 250 253 253 253 253 253 253 238 238 238 138 138 138 +97 87 74 110 93 64 119 103 79 103 95 84 108 105 106 238 238 238 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 82 82 82 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 2 2 6 2 2 6 78 78 78 70 70 70 34 34 34 +14 14 14 6 6 6 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 14 14 +34 34 34 66 66 66 78 78 78 6 6 6 2 2 6 18 18 18 +218 218 218 253 253 253 253 253 253 210 210 210 80 72 61 103 75 39 +169 127 44 169 127 44 167 127 47 207 184 137 167 127 47 104 86 63 +234 234 234 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 182 182 182 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 2 2 6 2 2 6 18 18 18 90 90 90 62 62 62 +30 30 30 10 10 10 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 10 10 10 26 26 26 +58 58 58 90 90 90 18 18 18 2 2 6 2 2 6 108 105 106 +253 253 253 253 253 253 218 218 218 103 75 39 169 127 44 143 114 43 +118 84 23 169 127 44 169 127 44 188 164 115 183 152 86 169 127 44 +97 87 74 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 231 231 231 18 18 18 2 2 6 2 2 6 2 2 6 +2 2 6 2 2 6 2 2 6 2 2 6 18 18 18 94 94 94 +54 54 54 26 26 26 10 10 10 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 6 6 6 22 22 22 50 50 50 +90 90 90 26 26 26 2 2 6 2 2 6 14 14 14 195 195 195 +250 250 250 253 253 253 89 81 69 166 123 43 169 127 44 169 127 44 +98 77 21 163 123 43 120 94 48 156 118 43 225 216 182 161 124 51 +80 72 61 171 170 170 253 253 253 253 253 253 253 253 253 253 253 253 +250 250 250 242 242 242 54 54 54 2 2 6 2 2 6 2 2 6 +2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 38 38 38 +86 86 86 50 50 50 22 22 22 6 6 6 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 6 6 6 14 14 14 38 38 38 82 82 82 +34 34 34 2 2 6 2 2 6 2 2 6 42 42 42 195 195 195 +246 246 246 231 231 231 104 86 51 169 127 44 169 127 44 169 127 44 +121 87 25 105 82 38 72 65 62 105 82 38 211 190 148 239 232 219 +154 139 110 89 81 69 210 210 210 250 250 250 246 246 246 238 238 238 +226 226 226 231 231 231 101 101 101 6 6 6 2 2 6 2 2 6 +2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 +38 38 38 82 82 82 42 42 42 14 14 14 6 6 6 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 10 10 10 26 26 26 62 62 62 66 66 66 +2 2 6 2 2 6 2 2 6 6 6 6 70 70 70 171 170 170 +206 206 206 101 98 89 157 117 39 169 127 44 169 127 44 169 127 44 +163 123 43 105 82 38 163 123 43 169 127 44 211 190 148 255 255 255 +255 255 255 253 253 253 144 144 144 90 90 90 190 190 190 202 202 202 +198 198 198 202 202 202 182 182 182 18 18 18 2 2 6 2 2 6 +2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 62 62 62 66 66 66 30 30 30 10 10 10 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 14 14 14 42 42 42 82 82 82 18 18 18 +2 2 6 2 2 6 2 2 6 10 10 10 94 94 94 182 182 182 +210 210 210 99 81 49 169 127 44 169 127 44 169 127 44 169 127 44 +169 127 44 98 77 21 169 127 44 188 164 115 252 252 252 255 255 255 +255 255 255 255 255 255 255 255 255 158 155 152 108 105 106 138 138 138 +195 195 195 195 195 195 210 210 210 158 158 158 6 6 6 14 14 14 +50 50 50 14 14 14 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 6 6 6 86 86 86 46 46 46 18 18 18 6 6 6 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 6 6 6 22 22 22 54 54 54 70 70 70 2 2 6 +2 2 6 10 10 10 2 2 6 22 22 22 169 168 166 231 231 231 +128 128 128 143 114 43 169 127 44 169 127 44 169 127 44 169 127 44 +169 127 44 118 84 23 167 127 47 247 245 242 255 255 255 255 255 255 +255 255 255 255 255 255 182 182 182 116 116 116 128 128 128 38 38 38 +206 206 206 206 206 206 198 198 198 226 226 226 94 94 94 2 2 6 +6 6 6 38 38 38 30 30 30 2 2 6 2 2 6 2 2 6 +2 2 6 2 2 6 62 62 62 66 66 66 26 26 26 10 10 10 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 10 10 10 30 30 30 74 74 74 50 50 50 2 2 6 +26 26 26 26 26 26 2 2 6 106 106 106 238 238 238 253 253 253 +89 81 66 169 127 44 169 127 44 169 127 44 169 127 44 169 127 44 +169 127 44 157 117 39 138 113 67 255 255 255 255 255 255 255 255 255 +255 255 255 255 255 255 202 202 202 50 50 50 34 34 34 50 50 50 +231 231 231 246 246 246 218 218 218 202 202 202 210 210 210 14 14 14 +2 2 6 2 2 6 30 30 30 22 22 22 2 2 6 2 2 6 +2 2 6 2 2 6 18 18 18 86 86 86 42 42 42 14 14 14 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 14 14 14 42 42 42 90 90 90 22 22 22 2 2 6 +42 42 42 2 2 6 18 18 18 218 218 218 253 253 253 253 253 253 +104 86 63 169 127 44 169 127 44 169 127 44 169 127 44 169 127 44 +166 123 43 156 118 43 105 82 38 255 255 255 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 255 221 221 221 182 182 182 90 90 90 +232 232 232 253 253 253 248 248 248 221 221 221 218 218 218 101 101 101 +2 2 6 14 14 14 18 18 18 38 38 38 10 10 10 2 2 6 +2 2 6 2 2 6 2 2 6 78 78 78 58 58 58 22 22 22 +6 6 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +6 6 6 18 18 18 54 54 54 82 82 82 2 2 6 26 26 26 +22 22 22 2 2 6 123 123 123 253 253 253 253 253 253 253 253 253 +89 81 66 169 127 44 169 127 44 169 127 44 169 127 44 166 123 43 +141 109 45 157 117 39 118 84 23 240 237 231 255 255 255 255 255 255 +255 255 255 255 255 255 255 255 255 226 226 226 97 87 74 72 54 38 +253 253 253 253 253 253 251 251 250 250 250 250 238 238 238 198 198 198 +6 6 6 38 38 38 58 58 58 26 26 26 38 38 38 2 2 6 +2 2 6 2 2 6 2 2 6 46 46 46 78 78 78 30 30 30 +10 10 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +10 10 10 30 30 30 74 74 74 58 58 58 2 2 6 42 42 42 +2 2 6 22 22 22 231 231 231 253 253 253 253 253 253 253 253 253 +162 161 160 104 86 51 169 127 44 169 127 44 156 118 43 141 109 45 +145 111 45 105 82 38 157 117 39 216 197 160 255 255 255 203 202 203 +158 155 152 116 116 116 72 65 62 95 51 32 175 118 6 145 90 47 +250 250 250 253 253 253 252 252 252 253 253 253 253 253 253 246 246 246 +46 46 46 38 38 38 42 42 42 14 14 14 38 38 38 14 14 14 +2 2 6 2 2 6 2 2 6 6 6 6 86 86 86 46 46 46 +14 14 14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 6 6 +14 14 14 42 42 42 90 90 90 18 18 18 18 18 18 26 26 26 +2 2 6 116 116 116 253 253 253 253 253 253 253 253 253 253 253 253 +248 248 248 138 138 138 99 81 49 105 82 38 125 96 44 121 87 25 +98 77 21 125 96 44 169 127 44 216 197 160 255 255 255 190 190 190 +108 105 106 70 70 70 58 58 58 72 54 38 192 133 9 120 94 48 +250 250 250 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +94 94 94 6 6 6 2 2 6 2 2 6 10 10 10 34 34 34 +2 2 6 2 2 6 2 2 6 2 2 6 74 74 74 58 58 58 +22 22 22 6 6 6 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 10 10 +26 26 26 66 66 66 82 82 82 2 2 6 38 38 38 6 6 6 +14 14 14 210 210 210 253 253 253 253 253 253 253 253 253 253 253 253 +101 101 101 90 90 90 244 244 244 89 81 69 145 111 45 161 124 51 +197 178 128 207 184 137 216 197 160 218 218 218 171 170 170 151 148 137 +141 130 107 80 72 61 169 168 166 226 226 226 109 101 93 187 187 187 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +144 144 144 2 2 6 2 2 6 2 2 6 2 2 6 46 46 46 +2 2 6 2 2 6 2 2 6 2 2 6 42 42 42 74 74 74 +30 30 30 10 10 10 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 6 6 6 14 14 14 +42 42 42 90 90 90 26 26 26 6 6 6 42 42 42 2 2 6 +74 74 74 250 250 250 253 253 253 253 253 253 253 253 253 253 253 253 +106 106 106 131 116 91 210 209 209 110 93 64 169 127 44 233 229 220 +255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 239 232 219 +169 127 44 120 94 48 244 244 244 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +182 182 182 2 2 6 2 2 6 2 2 6 2 2 6 46 46 46 +2 2 6 2 2 6 2 2 6 2 2 6 10 10 10 86 86 86 +38 38 38 10 10 10 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 10 10 10 26 26 26 +66 66 66 82 82 82 2 2 6 22 22 22 18 18 18 2 2 6 +144 144 144 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +171 170 170 125 96 44 103 95 84 110 93 64 175 146 61 255 255 255 +255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 +183 152 86 145 111 45 195 195 194 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 252 252 252 253 253 253 253 253 253 253 253 253 +206 206 206 2 2 6 2 2 6 2 2 6 2 2 6 38 38 38 +2 2 6 2 2 6 2 2 6 2 2 6 6 6 6 86 86 86 +46 46 46 14 14 14 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 6 6 6 18 18 18 46 46 46 +86 86 86 18 18 18 2 2 6 34 34 34 10 10 10 6 6 6 +210 210 210 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +246 246 246 100 88 68 145 111 45 120 94 48 159 135 84 249 249 249 +255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 +152 137 111 156 118 43 158 158 158 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 251 251 250 253 253 253 253 253 253 253 253 253 +221 221 221 6 6 6 2 2 6 2 2 6 6 6 6 30 30 30 +2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 82 82 82 +54 54 54 18 18 18 6 6 6 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 10 10 10 26 26 26 66 66 66 +62 62 62 2 2 6 2 2 6 38 38 38 10 10 10 26 26 26 +238 238 238 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 144 144 144 138 113 67 166 123 43 138 119 85 246 246 246 +255 255 255 242 242 242 255 255 255 255 255 255 234 234 234 255 255 255 +152 137 111 156 118 43 162 161 160 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 250 250 250 253 253 253 253 253 253 253 253 253 +231 231 231 6 6 6 2 2 6 2 2 6 10 10 10 30 30 30 +2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 66 66 66 +58 58 58 22 22 22 6 6 6 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 10 10 10 38 38 38 78 78 78 +6 6 6 2 2 6 2 2 6 46 46 46 14 14 14 42 42 42 +246 246 246 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 250 250 250 94 90 86 154 122 46 138 119 85 253 253 253 +244 243 244 195 195 194 255 255 255 244 244 244 182 182 182 255 255 255 +147 140 128 145 111 45 187 187 187 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 249 249 249 249 249 249 253 253 253 253 253 253 +234 234 234 10 10 10 2 2 6 2 2 6 22 22 22 14 14 14 +2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 66 66 66 +62 62 62 22 22 22 6 6 6 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 6 6 6 18 18 18 50 50 50 74 74 74 +2 2 6 2 2 6 14 14 14 70 70 70 34 34 34 62 62 62 +250 250 250 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 231 231 231 104 86 63 141 130 107 255 255 255 +246 246 246 182 182 182 255 255 255 231 231 231 196 195 196 255 255 255 +147 140 128 119 100 63 242 242 242 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 242 242 242 253 253 253 253 253 253 +234 234 234 14 14 14 2 2 6 2 2 6 30 30 30 2 2 6 +2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 66 66 66 +62 62 62 22 22 22 6 6 6 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 6 6 6 18 18 18 54 54 54 62 62 62 +2 2 6 2 2 6 2 2 6 30 30 30 46 46 46 70 70 70 +250 250 250 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 210 210 210 89 81 66 152 137 111 255 255 255 +254 254 254 171 170 170 255 255 255 218 218 218 210 209 209 255 255 255 +151 148 137 123 99 54 169 168 166 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 252 252 252 253 253 253 253 253 253 +226 226 226 10 10 10 2 2 6 6 6 6 30 30 30 2 2 6 +2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 66 66 66 +58 58 58 22 22 22 6 6 6 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 6 6 6 22 22 22 58 58 58 62 62 62 +2 2 6 2 2 6 2 2 6 2 2 6 30 30 30 78 78 78 +250 250 250 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 249 249 249 97 87 74 169 127 44 152 137 111 255 255 255 +255 255 255 171 170 170 255 255 255 203 202 203 226 226 226 255 255 255 +158 155 152 169 127 44 120 94 48 234 234 234 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +206 206 206 2 2 6 22 22 22 34 34 34 18 14 6 22 22 22 +26 26 26 18 18 18 6 6 6 2 2 6 2 2 6 82 82 82 +54 54 54 18 18 18 6 6 6 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 6 6 6 26 26 26 62 62 62 106 106 106 +78 53 15 185 133 11 210 162 10 121 92 8 6 6 6 62 62 62 +238 238 238 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 238 238 238 110 93 64 166 123 43 162 161 160 255 255 255 +255 255 255 108 105 106 94 94 94 82 82 82 244 244 244 255 255 255 +202 202 202 150 121 62 141 109 45 202 202 202 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +158 158 158 18 18 18 14 14 14 2 2 6 2 2 6 2 2 6 +6 6 6 18 18 18 66 66 66 38 38 38 6 6 6 94 94 94 +50 50 50 18 18 18 6 6 6 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 6 6 +10 10 10 10 10 10 18 18 18 38 38 38 78 78 78 141 130 107 +216 158 10 242 186 14 246 190 14 246 190 14 163 110 8 10 10 10 +90 90 90 238 238 238 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 252 252 252 144 144 144 80 72 61 218 218 218 255 255 255 +206 206 206 123 123 123 253 253 253 128 128 128 169 168 166 254 254 254 +254 254 254 101 98 89 128 128 128 252 252 252 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 225 216 182 219 191 94 219 191 94 +180 133 36 37 26 9 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 2 2 6 38 38 38 46 46 46 26 26 26 106 106 106 +54 54 54 18 18 18 6 6 6 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 6 6 6 14 14 14 22 22 22 +30 30 30 38 38 38 50 50 50 70 70 70 106 106 106 190 142 34 +226 170 11 242 186 14 246 190 14 246 190 14 246 190 14 156 107 11 +6 6 6 74 74 74 226 226 226 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 182 182 182 108 105 106 116 116 116 +123 123 123 244 244 244 253 253 253 244 244 244 128 128 128 116 116 116 +116 116 116 169 168 166 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 201 174 68 241 196 14 241 208 19 +232 195 16 38 30 10 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 6 6 6 30 30 30 26 26 26 210 162 10 159 135 84 +66 66 66 26 26 26 6 6 6 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 6 6 6 18 18 18 38 38 38 58 58 58 +78 78 78 86 86 86 101 101 101 123 123 123 175 146 61 210 150 10 +234 174 13 246 186 14 246 190 14 246 190 14 246 190 14 236 186 11 +104 69 6 2 2 6 46 46 46 198 198 198 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 234 234 234 242 242 242 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 201 174 68 242 186 14 241 196 14 +210 162 10 22 18 6 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 2 2 6 6 6 6 121 92 8 238 202 15 232 195 16 +82 82 82 34 34 34 10 10 10 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 14 14 14 38 38 38 70 70 70 154 122 46 +190 142 34 200 144 11 197 138 11 197 138 11 213 154 11 226 170 11 +242 186 14 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +225 175 15 45 32 11 2 2 6 22 22 22 158 158 158 250 250 250 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 250 250 250 242 242 242 201 174 68 239 182 13 236 186 11 +213 154 11 45 32 11 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 2 2 6 61 42 6 225 175 15 236 186 11 236 186 11 +119 103 79 42 42 42 14 14 14 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 6 6 6 22 22 22 54 54 54 154 122 46 213 154 11 +226 170 11 230 174 11 226 170 11 226 170 11 236 178 12 242 186 14 +246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +241 196 14 184 144 12 10 10 10 2 2 6 6 6 6 116 116 116 +242 242 242 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 231 231 231 198 198 198 201 174 68 236 178 12 236 178 12 +210 150 10 137 92 6 18 14 6 2 2 6 2 2 6 2 2 6 +6 6 6 61 42 6 200 144 11 236 178 12 239 182 13 239 182 13 +124 112 88 58 58 58 22 22 22 6 6 6 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 10 10 10 30 30 30 70 70 70 180 133 36 226 170 11 +239 182 13 242 186 14 242 186 14 246 186 14 246 190 14 246 190 14 +246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 232 195 16 104 69 6 2 2 6 2 2 6 2 2 6 +66 66 66 221 221 221 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 206 206 206 198 198 198 201 174 68 230 174 11 230 174 11 +216 158 10 192 133 9 163 110 8 116 81 8 104 69 6 116 81 8 +167 114 7 197 138 11 226 170 11 239 182 13 242 186 14 242 186 14 +165 145 105 78 78 78 34 34 34 14 14 14 6 6 6 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 6 6 6 30 30 30 78 78 78 190 142 34 226 170 11 +239 182 13 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 241 196 14 210 162 10 22 18 6 2 2 6 2 2 6 +2 2 6 38 38 38 218 218 218 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +250 250 250 206 206 206 198 198 198 201 174 68 226 170 11 236 178 12 +224 166 10 210 150 10 200 144 11 197 138 11 192 133 9 197 138 11 +210 150 10 226 170 11 242 186 14 246 190 14 246 190 14 246 186 14 +225 175 15 124 112 88 62 62 62 30 30 30 14 14 14 6 6 6 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 10 10 10 30 30 30 78 78 78 167 127 47 224 166 10 +239 182 13 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 241 196 14 138 101 21 2 2 6 2 2 6 +2 2 6 2 2 6 78 78 78 250 250 250 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +250 250 250 218 218 218 198 198 198 190 142 34 219 162 10 236 178 12 +234 174 13 224 166 10 216 158 10 213 154 11 213 154 11 216 158 10 +226 170 11 239 182 13 246 190 14 246 190 14 246 190 14 246 190 14 +242 186 14 197 166 45 101 101 101 58 58 58 30 30 30 14 14 14 +6 6 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 10 10 10 30 30 30 74 74 74 167 127 47 216 158 10 +236 178 12 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 241 196 14 226 184 13 61 42 6 2 2 6 +2 2 6 2 2 6 22 22 22 238 238 238 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 226 226 226 187 187 187 180 133 36 216 158 10 236 178 12 +239 182 13 236 178 12 230 174 11 226 170 11 226 170 11 230 174 11 +236 178 12 242 186 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 186 14 239 182 13 197 166 45 106 106 106 66 66 66 34 34 34 +14 14 14 6 6 6 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 6 6 6 26 26 26 70 70 70 163 133 67 213 154 11 +236 178 12 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 246 190 14 241 196 14 190 146 13 18 14 6 +2 2 6 2 2 6 46 46 46 246 246 246 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 221 221 221 86 86 86 156 107 11 216 158 10 236 178 12 +242 186 14 246 186 14 242 186 14 239 182 13 239 182 13 242 186 14 +242 186 14 246 186 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 242 186 14 225 175 15 142 114 66 66 66 66 +30 30 30 10 10 10 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 6 6 6 26 26 26 70 70 70 163 133 67 210 150 10 +236 178 12 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 246 190 14 246 190 14 232 195 16 121 92 8 +34 34 34 106 106 106 221 221 221 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +242 242 242 82 82 82 18 14 6 163 110 8 216 158 10 236 178 12 +242 186 14 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 246 190 14 246 190 14 242 186 14 163 133 67 +46 46 46 18 18 18 6 6 6 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 10 10 10 30 30 30 78 78 78 163 133 67 210 150 10 +236 178 12 246 186 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 246 190 14 246 190 14 241 196 14 225 175 15 +181 170 141 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 218 218 218 +58 58 58 2 2 6 22 18 6 167 114 7 216 158 10 236 178 12 +246 186 14 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 246 190 14 246 186 14 242 186 14 190 142 34 +54 54 54 22 22 22 6 6 6 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 14 14 14 38 38 38 86 86 86 180 133 36 213 154 11 +236 178 12 246 186 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 232 195 16 +190 146 13 218 218 218 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 250 250 250 171 170 170 26 26 26 +2 2 6 2 2 6 37 26 9 163 110 8 219 162 10 239 182 13 +246 186 14 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 246 186 14 236 178 12 224 166 10 142 114 66 +46 46 46 18 18 18 6 6 6 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +6 6 6 18 18 18 50 50 50 109 101 93 192 133 9 224 166 10 +242 186 14 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 246 190 14 246 190 14 242 186 14 226 184 13 +210 162 10 141 109 45 226 226 226 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 253 253 253 198 198 198 66 66 66 2 2 6 2 2 6 +2 2 6 2 2 6 45 32 11 156 107 11 219 162 10 239 182 13 +246 186 14 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 242 186 14 234 174 13 213 154 11 154 122 46 66 66 66 +30 30 30 10 10 10 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +6 6 6 22 22 22 58 58 58 150 121 62 206 145 10 234 174 13 +242 186 14 246 186 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 246 190 14 246 190 14 246 186 14 236 178 12 +210 162 10 163 110 8 61 42 6 138 138 138 218 218 218 250 250 250 +253 253 253 253 253 253 253 253 253 250 250 250 242 242 242 210 210 210 +144 144 144 66 66 66 6 6 6 2 2 6 2 2 6 2 2 6 +2 2 6 2 2 6 61 42 6 163 110 8 216 158 10 236 178 12 +246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 239 182 13 +230 174 11 216 158 10 190 142 34 124 112 88 70 70 70 38 38 38 +18 18 18 6 6 6 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +6 6 6 22 22 22 62 62 62 166 123 43 206 145 10 224 166 10 +236 178 12 239 182 13 242 186 14 242 186 14 246 186 14 246 190 14 +246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 236 178 12 +216 158 10 175 118 6 78 53 15 2 2 6 6 6 6 30 30 30 +54 54 54 62 62 62 50 50 50 38 38 38 14 14 14 2 2 6 +2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 6 6 6 78 53 15 167 114 7 213 154 11 236 178 12 +246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 242 186 14 239 182 13 239 182 13 230 174 11 210 150 10 +167 127 47 124 112 88 82 82 82 54 54 54 34 34 34 18 18 18 +6 6 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +6 6 6 18 18 18 50 50 50 157 117 39 192 133 9 200 144 11 +216 158 10 219 162 10 224 166 10 226 170 11 230 174 11 236 178 12 +239 182 13 239 182 13 242 186 14 246 186 14 246 190 14 246 190 14 +246 190 14 246 190 14 246 190 14 246 190 14 246 186 14 230 174 11 +210 150 10 163 110 8 104 69 6 10 10 10 2 2 6 2 2 6 +2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 6 6 6 85 60 19 167 114 7 206 145 10 230 174 11 +242 186 14 246 190 14 246 190 14 246 190 14 246 186 14 242 186 14 +239 182 13 230 174 11 224 166 10 213 154 11 180 133 36 124 112 88 +86 86 86 58 58 58 38 38 38 22 22 22 10 10 10 6 6 6 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 14 14 14 34 34 34 70 70 70 141 109 45 157 117 39 +167 114 7 180 123 7 192 133 9 197 138 11 200 144 11 206 145 10 +213 154 11 219 162 10 224 166 10 230 174 11 239 182 13 242 186 14 +246 186 14 246 186 14 246 186 14 246 186 14 239 182 13 216 158 10 +185 133 11 152 99 6 104 69 6 18 14 6 2 2 6 2 2 6 +2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 2 2 6 +2 2 6 6 6 6 78 53 15 152 99 6 192 133 9 219 162 10 +236 178 12 239 182 13 246 186 14 242 186 14 239 182 13 236 178 12 +224 166 10 206 145 10 192 133 9 150 121 62 94 94 94 62 62 62 +42 42 42 22 22 22 14 14 14 6 6 6 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 6 6 6 18 18 18 34 34 34 58 58 58 78 78 78 +101 98 89 124 112 88 141 109 45 156 107 11 163 110 8 167 114 7 +175 118 6 180 123 7 185 133 11 197 138 11 210 150 10 219 162 10 +226 170 11 236 178 12 236 178 12 234 174 13 219 162 10 197 138 11 +163 110 8 137 92 6 85 60 19 10 10 10 2 2 6 2 2 6 +18 18 18 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 +38 38 38 38 38 38 38 38 38 38 38 38 26 26 26 2 2 6 +2 2 6 6 6 6 61 42 6 137 92 6 175 118 6 200 144 11 +219 162 10 230 174 11 234 174 13 230 174 11 219 162 10 210 150 10 +192 133 9 163 110 8 124 112 88 82 82 82 50 50 50 30 30 30 +14 14 14 6 6 6 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 6 6 6 14 14 14 22 22 22 34 34 34 +42 42 42 58 58 58 74 74 74 86 86 86 101 98 89 119 100 63 +125 96 44 121 87 25 137 92 6 152 99 6 163 110 8 180 123 7 +185 133 11 197 138 11 206 145 10 200 144 11 180 123 7 156 107 11 +137 92 6 104 69 6 45 32 11 54 54 54 108 105 106 101 98 89 +86 86 86 82 82 82 78 78 78 78 78 78 78 78 78 78 78 78 +78 78 78 78 78 78 78 78 78 82 82 82 86 86 86 94 94 94 +106 106 106 101 101 101 86 66 32 116 81 8 156 107 11 180 123 7 +192 133 9 200 144 11 206 145 10 200 144 11 192 133 9 175 118 6 +138 101 21 109 101 93 70 70 70 42 42 42 22 22 22 10 10 10 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 6 6 6 10 10 10 +14 14 14 22 22 22 30 30 30 38 38 38 50 50 50 62 62 62 +74 74 74 90 90 90 101 98 89 119 103 79 121 87 25 116 81 8 +137 92 6 152 99 6 152 99 6 152 99 6 137 92 6 116 81 8 +104 69 6 86 66 32 101 98 89 82 82 82 58 58 58 46 46 46 +38 38 38 34 34 34 34 34 34 34 34 34 34 34 34 34 34 34 +34 34 34 34 34 34 34 34 34 34 34 34 38 38 38 42 42 42 +54 54 54 82 82 82 97 87 74 85 60 19 137 92 6 156 107 11 +167 114 7 175 118 6 175 118 6 167 114 7 152 99 6 121 87 25 +101 98 89 62 62 62 34 34 34 18 18 18 6 6 6 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 6 6 6 6 6 6 10 10 10 18 18 18 22 22 22 +30 30 30 42 42 42 50 50 50 66 66 66 86 86 86 101 98 89 +104 86 63 104 69 6 104 69 6 104 69 6 104 69 6 85 60 19 +86 66 32 90 90 90 62 62 62 38 38 38 22 22 22 14 14 14 +10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 +6 6 6 10 10 10 10 10 10 10 10 10 10 10 10 14 14 14 +22 22 22 42 42 42 70 70 70 89 81 66 78 53 15 104 69 6 +116 81 8 137 92 6 137 92 6 116 81 8 99 81 49 86 86 86 +58 58 58 30 30 30 14 14 14 6 6 6 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 6 6 +10 10 10 14 14 14 18 18 18 26 26 26 38 38 38 54 54 54 +70 70 70 86 86 86 97 87 74 89 81 66 89 81 66 86 86 86 +74 74 74 50 50 50 30 30 30 14 14 14 6 6 6 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +6 6 6 18 18 18 34 34 34 58 58 58 82 82 82 89 81 66 +89 81 66 89 81 66 100 88 68 97 87 74 74 74 74 50 50 50 +26 26 26 14 14 14 6 6 6 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 6 6 6 6 6 6 14 14 14 18 18 18 +30 30 30 38 38 38 46 46 46 54 54 54 50 50 50 42 42 42 +30 30 30 18 18 18 10 10 10 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 6 6 6 14 14 14 26 26 26 38 38 38 50 50 50 +58 58 58 58 58 58 54 54 54 42 42 42 30 30 30 18 18 18 +10 10 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 6 6 +6 6 6 10 10 10 14 14 14 18 18 18 18 18 18 14 14 14 +10 10 10 6 6 6 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 6 6 6 14 14 14 18 18 18 +22 22 22 22 22 22 18 18 18 14 14 14 10 10 10 6 6 6 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 diff --git a/include/linux/linux_logo.h b/include/linux/linux_logo.h index d4d5b93efe843..a9560bb676cb6 100644 --- a/include/linux/linux_logo.h +++ b/include/linux/linux_logo.h @@ -45,6 +45,7 @@ extern const struct linux_logo logo_superh_mono; extern const struct linux_logo logo_superh_vga16; extern const struct linux_logo logo_superh_clut224; extern const struct linux_logo logo_spe_clut224; +extern const struct linux_logo logo_beagle_clut224; extern const struct linux_logo *fb_find_logo(int depth); #ifdef CONFIG_FB_LOGO_EXTRA From 048419d2ddfa93fc030f8c6ff330d0944323ad09 Mon Sep 17 00:00:00 2001 From: Juerg Haefliger Date: Tue, 10 Aug 2021 08:13:50 +0200 Subject: [PATCH 59/65] ARM: Fix instruction set selection for GCC 11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GCC 11 on ARM now complains like the following when trying to determine if an arch is supported. Presumably because it enforces the default option '--with-float=hard' which GCC 10 didn't do? $ arm-linux-gnueabihf-gcc-11 -march=armv7-a -c -x c /dev/null cc1: error: ‘-mfloat-abi=hard’: selected architecture lacks an FPU Due to that, the kernel build system selects the wrong compiler options which throws errros like this during the build: /tmp/ccrHfZPj.s: Assembler messages: /tmp/ccrHfZPj.s:116: Error: selected processor does not support `dmb ish' in ARM mode /tmp/ccrHfZPj.s:150: Error: selected processor does not support `isb ' in ARM mode /tmp/ccrHfZPj.s:160: Error: selected processor does not support `mrrc p15,1,r4,r5,c14' in ARM mode /tmp/ccrHfZPj.s:245: Error: selected processor does not support `dmb ish' in ARM mode /tmp/ccrHfZPj.s:503: Error: selected processor does not support `dmb ish' in ARM mode /tmp/ccrHfZPj.s:527: Error: selected processor does not support `dmb ish' in ARM mode /tmp/ccrHfZPj.s:698: Error: selected processor does not support `dmb ish' in ARM mode /tmp/ccrHfZPj.s:731: Error: selected processor does not support `isb ' in ARM mode Fix that by adding '-msoft-float' to KBUILD_CFLAGS before the definition of the 'arch-$(CONFIG_CPU_)' instruction selection macros. Signed-off-by: Juerg Haefliger --- arch/arm/Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 0e5a8765e60b3..42fec49433581 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -56,6 +56,9 @@ endif # KBUILD_CFLAGS += $(call cc-option,-fno-ipa-sra) +# Need -msoft-float for gcc 11 for the below instruction set selection +KBUILD_CFLAGS += -msoft-float + # This selects which instruction set is used. # Note that GCC does not numerically define an architecture version # macro, but instead defines a whole series of macros which makes @@ -125,7 +128,7 @@ AFLAGS_ISA :=$(CFLAGS_ISA) endif # Need -Uarm for gcc < 3.x -KBUILD_CFLAGS +=$(CFLAGS_ABI) $(CFLAGS_ISA) $(arch-y) $(tune-y) $(call cc-option,-mshort-load-bytes,$(call cc-option,-malignment-traps,)) -msoft-float -Uarm +KBUILD_CFLAGS +=$(CFLAGS_ABI) $(CFLAGS_ISA) $(arch-y) $(tune-y) $(call cc-option,-mshort-load-bytes,$(call cc-option,-malignment-traps,)) -Uarm KBUILD_AFLAGS +=$(CFLAGS_ABI) $(AFLAGS_ISA) $(arch-y) $(tune-y) -include asm/unified.h -msoft-float CHECKFLAGS += -D__arm__ From f48513a7bd7770bc15a122517991c969995a8503 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Fri, 3 Feb 2023 19:28:34 -0600 Subject: [PATCH 60/65] backports: bindeb-pkg: from: linux.git Reference: v5.19.17 Signed-off-by: Robert Nelson --- scripts/package/builddeb | 2 +- scripts/package/buildtar | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/scripts/package/builddeb b/scripts/package/builddeb index 91a502bb97e8a..67cd420dcf890 100755 --- a/scripts/package/builddeb +++ b/scripts/package/builddeb @@ -67,7 +67,7 @@ deploy_kernel_headers () { ) > debian/hdrsrcfiles { - if is_enabled CONFIG_STACK_VALIDATION; then + if is_enabled CONFIG_OBJTOOL; then echo tools/objtool/objtool fi diff --git a/scripts/package/buildtar b/scripts/package/buildtar index 936198a90477f..cb54c7f1aa806 100755 --- a/scripts/package/buildtar +++ b/scripts/package/buildtar @@ -39,6 +39,10 @@ case "${1}" in opts="-I ${XZ}" tarball=${tarball}.xz ;; + tarzst-pkg) + opts="-I ${ZSTD}" + tarball=${tarball}.zst + ;; *) echo "Unknown tarball target \"${1}\" requested, please add it to ${0}." >&2 exit 1 @@ -125,6 +129,14 @@ case "${ARCH}" in fi done ;; + riscv) + for i in Image.bz2 Image.gz Image; do + if [ -f "${objtree}/arch/riscv/boot/${i}" ] ; then + cp -v -- "${objtree}/arch/riscv/boot/${i}" "${tmpdir}/boot/vmlinux-${KERNELRELEASE}" + break + fi + done + ;; *) [ -f "${KBUILD_IMAGE}" ] && cp -v -- "${KBUILD_IMAGE}" "${tmpdir}/boot/vmlinux-kbuild-${KERNELRELEASE}" echo "" >&2 From 91bee37d5c72da027544974e7a4afcb8a1112cd1 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Fri, 6 Mar 2020 16:53:00 -0600 Subject: [PATCH 61/65] builddeb: Install our dtbs under /boot/dtbs/$version Signed-off-by: Robert Nelson --- scripts/package/builddeb | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/package/builddeb b/scripts/package/builddeb index 67cd420dcf890..969ff561a0c5c 100755 --- a/scripts/package/builddeb +++ b/scripts/package/builddeb @@ -153,6 +153,7 @@ if is_enabled CONFIG_OF_EARLY_FLATTREE; then # Only some architectures with OF support have this target if [ -d "${srctree}/arch/$SRCARCH/boot/dts" ]; then $MAKE -f $srctree/Makefile INSTALL_DTBS_PATH="$tmpdir/usr/lib/$packagename" dtbs_install + $MAKE -f $srctree/Makefile INSTALL_DTBS_PATH="$tmpdir/boot/dtbs/$version" dtbs_install fi fi From d87f0c1128f62bcbf6aaf8114f063e7808292d57 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Sun, 6 Nov 2022 11:09:45 -0600 Subject: [PATCH 62/65] enable: Jenkins/gitlab-ci Signed-off-by: Robert Nelson --- .github/FUNDING.yml | 12 ++++++++++++ .github/ISSUE_TEMPLATE/bug_report.md | 20 ++++++++++++++++++++ .gitlab-ci.yml | 9 +++++++++ Jenkinsfile | 11 +++++++++++ README.md | 7 +++++++ jenkins_build.sh | 15 +++++++++++++++ 6 files changed, 74 insertions(+) create mode 100644 .github/FUNDING.yml create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .gitlab-ci.yml create mode 100644 Jenkinsfile create mode 100644 README.md create mode 100755 jenkins_build.sh diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000000000..8b20af1e3599a --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: beagleboard # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: beagleboard # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: https://paypal.me/beagleboard # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000000..94be17e338b34 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,20 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**Describe how to reproduce the bug** +List all the steps needed to reproduce the bug + +**REQUIRED INFORMATION** +Run this command and paste the output here: +``` +sudo /opt/scripts/tools/version.sh +``` diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000..0c6a052a140f9 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,9 @@ +image: robertcnelson/debian-bullseye-slim-linux-compile:latest + +build: + stage: build + script: + - ./jenkins_build.sh + artifacts: + paths: + - "linux-image*.deb" diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000000000..5d5776ff0e8b2 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,11 @@ +pipeline { + agent { label 'amd64'} + + stages { + stage('Build') { + steps { + sh '/bin/bash ./jenkins_build.sh' + } + } + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000000000..2f4ecd4335166 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# Gitlab CI + +| kernel | normal | rt | +|:---:|:---:|:---:| +|4.19 | [![pipeline status](https://git.beagleboard.org/beagleboard/linux/badges/4.19/pipeline.svg)](https://git.beagleboard.org/beagleboard/linux/-/pipelines?page=1&scope=all&ref=4.19) | [![pipeline status](https://git.beagleboard.org/beagleboard/linux/badges/4.19-rt/pipeline.svg)](https://git.beagleboard.org/beagleboard/linux/-/pipelines?page=1&scope=all&ref=4.19-rt) | +|5.4 | [![pipeline status](https://git.beagleboard.org/beagleboard/linux/badges/5.4/pipeline.svg)](https://git.beagleboard.org/beagleboard/linux/-/pipelines?page=1&scope=all&ref=5.4) | [![pipeline status](https://git.beagleboard.org/beagleboard/linux/badges/5.4-rt/pipeline.svg)](https://git.beagleboard.org/beagleboard/linux/-/pipelines?page=1&scope=all&ref=5.4-rt) | +|5.10 | [![pipeline status](https://git.beagleboard.org/beagleboard/linux/badges/5.10/pipeline.svg)](https://git.beagleboard.org/beagleboard/linux/-/pipelines?page=1&scope=all&ref=5.10) | [![pipeline status](https://git.beagleboard.org/beagleboard/linux/badges/5.10-rt/pipeline.svg)](https://git.beagleboard.org/beagleboard/linux/-/pipelines?page=1&scope=all&ref=5.10-rt) | diff --git a/jenkins_build.sh b/jenkins_build.sh new file mode 100755 index 0000000000000..5459c6250f154 --- /dev/null +++ b/jenkins_build.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +#git clone -b 5.10 https://github.com/beagleboard/linux --depth=10 +#cd ./linux + +CORES=$(getconf _NPROCESSORS_ONLN) + +export CC=/usr/bin/arm-linux-gnueabihf- + +make ARCH=arm CROSS_COMPILE=${CC} clean +make ARCH=arm CROSS_COMPILE=${CC} bb.org_defconfig + +echo "make -j${CORES} ARCH=arm KBUILD_DEBARCH=armhf CROSS_COMPILE=${CC} bindeb-pkg" +make -j${CORES} ARCH=arm KBUILD_DEBARCH=armhf KDEB_PKGVERSION=1xross CROSS_COMPILE=${CC} bindeb-pkg +mv ../*.deb ./ From 2d92307c66d9cf4fbf60b5c01e3e6efcf091e74b Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Sat, 4 Feb 2023 16:38:28 -0600 Subject: [PATCH 63/65] 5.10.162-ti-r56 patchset From 27db3d1799c45b5b4170a168a96d429aff579a8a Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Sat, 4 Feb 2023 16:38:57 -0600 Subject: [PATCH 64/65] 5.10.162-ti-r56 5.10.162-ti-r56 bb.org_defconfig 5.10 TI Delta: https://github.com/RobertCNelson/ti-linux-kernel/compare/06bd1d157b9b43163c1a2184e4a0c92f98656dd0...2927372e2c7cc3782c07b1896757962bc346d4c5 AUFS: https://github.com/sfjro/aufs-standalone/commit/72075d1d0bd59c94529348883e810410318674e5 BBDTBS: https://git.beagleboard.org/beagleboard/BeagleBoard-DeviceTrees/-/commit/fab7ca900247d3e9041cf526ea2c1fe284309987 TI_AMX3_CM3: http://git.ti.com/gitweb/?p=processor-firmware/ti-amx3-cm3-pm-firmware.git;a=commit;h=fb484c5e54f2e31cf0a338d2927a06a2870bcc2c WPANUSB: https://git.beagleboard.org/beagleconnect/linux/wpanusb/-/commit/6aa9bf65b9d88a2c9a111e7b4aed03de2be9413d BCFSERIAL: https://git.beagleboard.org/beagleconnect/linux/bcfserial/-/commit/db467023bd136c97c2e13c3a8b9e41dbdfafbc66 WIRELESS_REGDB: https://git.kernel.org/pub/scm/linux/kernel/git/sforshee/wireless-regdb.git/commit/?id=fe05cc94e070c71486047002fc2edf68f5f2a07a KSMBD: https://github.com/cifsd-team/ksmbd/commit/9912f7960ee50fdb42daa28d198dc214fb92983f Signed-off-by: Robert Nelson --- arch/arm/configs/bb.org_defconfig | 2801 +++++++++++++++++ .../arm/configs/ti_sdk_am3x_release_defconfig | 2083 ++++++++++++ .../configs/ti_sdk_dra7x_release_defconfig | 2094 ++++++++++++ 3 files changed, 6978 insertions(+) create mode 100644 arch/arm/configs/bb.org_defconfig create mode 100644 arch/arm/configs/ti_sdk_am3x_release_defconfig create mode 100644 arch/arm/configs/ti_sdk_dra7x_release_defconfig diff --git a/arch/arm/configs/bb.org_defconfig b/arch/arm/configs/bb.org_defconfig new file mode 100644 index 0000000000000..de5f05afda672 --- /dev/null +++ b/arch/arm/configs/bb.org_defconfig @@ -0,0 +1,2801 @@ +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_KERNEL_LZO=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_NO_HZ_IDLE=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_PSI=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_IKHEADERS=m +CONFIG_LOG_BUF_SHIFT=16 +CONFIG_MEMCG=y +CONFIG_BLK_CGROUP=y +CONFIG_CFS_BANDWIDTH=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_RDMA=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CPUSETS=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_PERF=y +CONFIG_CGROUP_BPF=y +CONFIG_NAMESPACES=y +CONFIG_USER_NS=y +CONFIG_CHECKPOINT_RESTORE=y +CONFIG_SCHED_AUTOGROUP=y +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_SYSFS_SYSCALL is not set +CONFIG_BPF_LSM=y +CONFIG_BPF_SYSCALL=y +CONFIG_BPF_UNPRIV_DEFAULT_OFF=y +CONFIG_USERFAULTFD=y +CONFIG_EMBEDDED=y +# CONFIG_COMPAT_BRK is not set +CONFIG_SLAB_FREELIST_RANDOM=y +CONFIG_SLAB_FREELIST_HARDENED=y +CONFIG_PROFILING=y +CONFIG_OMAP_RESET_CLOCKS=y +CONFIG_SOC_OMAP5=y +CONFIG_SOC_AM33XX=y +CONFIG_SOC_DRA7XX=y +CONFIG_SOC_HAS_OMAP2_SDRC=y +CONFIG_OMAP5_ERRATA_801819=y +CONFIG_ARM_THUMBEE=y +CONFIG_PL310_ERRATA_588369=y +CONFIG_PL310_ERRATA_727915=y +CONFIG_PL310_ERRATA_753970=y +CONFIG_ARM_ERRATA_430973=y +CONFIG_ARM_ERRATA_773022=y +CONFIG_ARM_ERRATA_814220=y +CONFIG_SMP=y +# CONFIG_ARM_CPU_TOPOLOGY is not set +CONFIG_MCPM=y +CONFIG_NR_CPUS=2 +CONFIG_ARM_PSCI=y +CONFIG_HZ_250=y +# CONFIG_ATAGS is not set +CONFIG_KEXEC=y +CONFIG_CRASH_DUMP=y +CONFIG_EFI=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=m +CONFIG_CPU_FREQ_GOV_USERSPACE=m +CONFIG_CPU_FREQ_GOV_ONDEMAND=m +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m +CONFIG_CPUFREQ_DT=m +# CONFIG_ARM_OMAP2PLUS_CPUFREQ is not set +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_ARM_CPUIDLE=y +CONFIG_ARM_PSCI_CPUIDLE=y +CONFIG_KERNEL_MODE_NEON=y +CONFIG_HIBERNATION=y +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_DEBUG=y +CONFIG_PM_ADVANCED_DEBUG=y +CONFIG_APM_EMULATION=y +CONFIG_ENERGY_MODEL=y +CONFIG_DMI_SYSFS=y +CONFIG_TRUSTED_FOUNDATIONS=y +CONFIG_EFI_BOOTLOADER_CONTROL=m +CONFIG_EFI_CAPSULE_LOADER=m +CONFIG_RESET_ATTACK_MITIGATION=y +CONFIG_CRYPTO_SHA1_ARM_NEON=m +CONFIG_CRYPTO_SHA256_ARM=m +CONFIG_CRYPTO_SHA512_ARM=m +CONFIG_CRYPTO_AES_ARM=m +CONFIG_CRYPTO_AES_ARM_BS=m +CONFIG_CRYPTO_GHASH_ARM_CE=m +CONFIG_CRYPTO_CHACHA20_NEON=y +CONFIG_CRYPTO_POLY1305_ARM=y +CONFIG_CRYPTO_NHPOLY1305_NEON=m +CONFIG_CRYPTO_CURVE25519_NEON=y +CONFIG_OPROFILE=m +CONFIG_KPROBES=y +CONFIG_JUMP_LABEL=y +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_COMPRESS=y +CONFIG_MODULE_COMPRESS_XZ=y +CONFIG_BLK_DEV_ZONED=y +CONFIG_BLK_DEV_THROTTLING=y +CONFIG_BLK_WBT=y +CONFIG_BLK_CGROUP_IOCOST=y +CONFIG_BLK_SED_OPAL=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_KARMA_PARTITION=y +CONFIG_MQ_IOSCHED_KYBER=m +CONFIG_IOSCHED_BFQ=m +CONFIG_BFQ_GROUP_IOSCHED=y +CONFIG_BINFMT_MISC=m +CONFIG_KSM=y +CONFIG_CLEANCACHE=y +CONFIG_FRONTSWAP=y +CONFIG_ZSWAP=y +CONFIG_Z3FOLD=m +CONFIG_ZSMALLOC=m +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_PACKET_DIAG=m +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=m +CONFIG_XFRM_USER=m +CONFIG_XFRM_INTERFACE=m +CONFIG_XFRM_SUB_POLICY=y +CONFIG_XFRM_STATISTICS=y +CONFIG_NET_KEY=m +CONFIG_NET_KEY_MIGRATE=y +CONFIG_XDP_SOCKETS=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_FIB_TRIE_STATS=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +CONFIG_NET_IPIP=m +CONFIG_NET_IPGRE_DEMUX=m +CONFIG_NET_IPGRE=m +CONFIG_NET_IPGRE_BROADCAST=y +CONFIG_IP_MROUTE=y +CONFIG_IP_MROUTE_MULTIPLE_TABLES=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +CONFIG_NET_IPVTI=m +CONFIG_NET_FOU_IP_TUNNELS=y +CONFIG_INET_AH=m +CONFIG_INET_ESP=m +CONFIG_INET_ESP_OFFLOAD=m +CONFIG_INET_IPCOMP=m +CONFIG_INET_DIAG=m +CONFIG_INET_UDP_DIAG=m +CONFIG_INET_RAW_DIAG=m +CONFIG_INET_DIAG_DESTROY=y +CONFIG_TCP_CONG_ADVANCED=y +CONFIG_TCP_CONG_HSTCP=m +CONFIG_TCP_CONG_HYBLA=m +CONFIG_TCP_CONG_NV=m +CONFIG_TCP_CONG_SCALABLE=m +CONFIG_TCP_CONG_LP=m +CONFIG_TCP_CONG_VENO=m +CONFIG_TCP_CONG_YEAH=m +CONFIG_TCP_CONG_ILLINOIS=m +CONFIG_TCP_CONG_DCTCP=m +CONFIG_TCP_CONG_CDG=m +CONFIG_TCP_CONG_BBR=m +CONFIG_TCP_MD5SIG=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=m +CONFIG_INET6_ESP=m +CONFIG_INET6_ESP_OFFLOAD=m +CONFIG_INET6_IPCOMP=m +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_ILA=m +CONFIG_IPV6_VTI=m +CONFIG_IPV6_SIT=m +CONFIG_IPV6_SIT_6RD=y +CONFIG_IPV6_GRE=m +CONFIG_IPV6_SUBTREES=y +CONFIG_IPV6_MROUTE=y +CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y +CONFIG_IPV6_PIMSM_V2=y +CONFIG_IPV6_SEG6_LWTUNNEL=y +CONFIG_IPV6_SEG6_HMAC=y +CONFIG_NETLABEL=y +CONFIG_MPTCP=y +CONFIG_NETWORK_PHY_TIMESTAMPING=y +CONFIG_NETFILTER=y +CONFIG_BRIDGE_NETFILTER=m +CONFIG_NF_CONNTRACK=m +CONFIG_NF_LOG_NETDEV=m +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_ZONES=y +CONFIG_NF_CONNTRACK_PROCFS=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CONNTRACK_TIMEOUT=y +CONFIG_NF_CONNTRACK_TIMESTAMP=y +CONFIG_NF_CONNTRACK_AMANDA=m +CONFIG_NF_CONNTRACK_FTP=m +CONFIG_NF_CONNTRACK_H323=m +CONFIG_NF_CONNTRACK_IRC=m +CONFIG_NF_CONNTRACK_NETBIOS_NS=m +CONFIG_NF_CONNTRACK_SNMP=m +CONFIG_NF_CONNTRACK_PPTP=m +CONFIG_NF_CONNTRACK_SANE=m +CONFIG_NF_CONNTRACK_SIP=m +CONFIG_NF_CONNTRACK_TFTP=m +CONFIG_NF_CT_NETLINK=m +CONFIG_NF_CT_NETLINK_TIMEOUT=m +CONFIG_NF_CT_NETLINK_HELPER=m +CONFIG_NETFILTER_NETLINK_GLUE_CT=y +CONFIG_NF_TABLES=m +CONFIG_NF_TABLES_INET=y +CONFIG_NF_TABLES_NETDEV=y +CONFIG_NFT_NUMGEN=m +CONFIG_NFT_CT=m +CONFIG_NFT_FLOW_OFFLOAD=m +CONFIG_NFT_COUNTER=m +CONFIG_NFT_CONNLIMIT=m +CONFIG_NFT_LOG=m +CONFIG_NFT_LIMIT=m +CONFIG_NFT_MASQ=m +CONFIG_NFT_REDIR=m +CONFIG_NFT_NAT=m +CONFIG_NFT_TUNNEL=m +CONFIG_NFT_OBJREF=m +CONFIG_NFT_QUEUE=m +CONFIG_NFT_QUOTA=m +CONFIG_NFT_REJECT=m +CONFIG_NFT_COMPAT=m +CONFIG_NFT_HASH=m +CONFIG_NFT_FIB_INET=m +CONFIG_NFT_XFRM=m +CONFIG_NFT_SOCKET=m +CONFIG_NFT_OSF=m +CONFIG_NFT_TPROXY=m +CONFIG_NFT_SYNPROXY=m +CONFIG_NFT_DUP_NETDEV=m +CONFIG_NFT_FWD_NETDEV=m +CONFIG_NFT_FIB_NETDEV=m +CONFIG_NF_FLOW_TABLE_INET=m +CONFIG_NF_FLOW_TABLE=m +CONFIG_NETFILTER_XT_SET=m +CONFIG_NETFILTER_XT_TARGET_AUDIT=m +CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m +CONFIG_NETFILTER_XT_TARGET_CONNMARK=m +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=m +CONFIG_NETFILTER_XT_TARGET_CT=m +CONFIG_NETFILTER_XT_TARGET_DSCP=m +CONFIG_NETFILTER_XT_TARGET_HMARK=m +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m +CONFIG_NETFILTER_XT_TARGET_LED=m +CONFIG_NETFILTER_XT_TARGET_LOG=m +CONFIG_NETFILTER_XT_TARGET_MARK=m +CONFIG_NETFILTER_XT_TARGET_NFLOG=m +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m +CONFIG_NETFILTER_XT_TARGET_TEE=m +CONFIG_NETFILTER_XT_TARGET_TPROXY=m +CONFIG_NETFILTER_XT_TARGET_TRACE=m +CONFIG_NETFILTER_XT_TARGET_SECMARK=m +CONFIG_NETFILTER_XT_TARGET_TCPMSS=m +CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m +CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m +CONFIG_NETFILTER_XT_MATCH_BPF=m +CONFIG_NETFILTER_XT_MATCH_CGROUP=m +CONFIG_NETFILTER_XT_MATCH_CLUSTER=m +CONFIG_NETFILTER_XT_MATCH_COMMENT=m +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m +CONFIG_NETFILTER_XT_MATCH_CONNLABEL=m +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m +CONFIG_NETFILTER_XT_MATCH_CONNMARK=m +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m +CONFIG_NETFILTER_XT_MATCH_CPU=m +CONFIG_NETFILTER_XT_MATCH_DEVGROUP=m +CONFIG_NETFILTER_XT_MATCH_DSCP=m +CONFIG_NETFILTER_XT_MATCH_ESP=m +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m +CONFIG_NETFILTER_XT_MATCH_HELPER=m +CONFIG_NETFILTER_XT_MATCH_IPCOMP=m +CONFIG_NETFILTER_XT_MATCH_IPRANGE=m +CONFIG_NETFILTER_XT_MATCH_IPVS=m +CONFIG_NETFILTER_XT_MATCH_LENGTH=m +CONFIG_NETFILTER_XT_MATCH_LIMIT=m +CONFIG_NETFILTER_XT_MATCH_MAC=m +CONFIG_NETFILTER_XT_MATCH_MARK=m +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m +CONFIG_NETFILTER_XT_MATCH_NFACCT=m +CONFIG_NETFILTER_XT_MATCH_OSF=m +CONFIG_NETFILTER_XT_MATCH_OWNER=m +CONFIG_NETFILTER_XT_MATCH_POLICY=m +CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m +CONFIG_NETFILTER_XT_MATCH_QUOTA=m +CONFIG_NETFILTER_XT_MATCH_RATEEST=m +CONFIG_NETFILTER_XT_MATCH_REALM=m +CONFIG_NETFILTER_XT_MATCH_RECENT=m +CONFIG_NETFILTER_XT_MATCH_SOCKET=m +CONFIG_NETFILTER_XT_MATCH_STATE=m +CONFIG_NETFILTER_XT_MATCH_STATISTIC=m +CONFIG_NETFILTER_XT_MATCH_STRING=m +CONFIG_NETFILTER_XT_MATCH_TCPMSS=m +CONFIG_NETFILTER_XT_MATCH_TIME=m +CONFIG_NETFILTER_XT_MATCH_U32=m +CONFIG_IP_SET=m +CONFIG_IP_SET_BITMAP_IP=m +CONFIG_IP_SET_BITMAP_IPMAC=m +CONFIG_IP_SET_BITMAP_PORT=m +CONFIG_IP_SET_HASH_IP=m +CONFIG_IP_SET_HASH_IPMARK=m +CONFIG_IP_SET_HASH_IPPORT=m +CONFIG_IP_SET_HASH_IPPORTIP=m +CONFIG_IP_SET_HASH_IPPORTNET=m +CONFIG_IP_SET_HASH_IPMAC=m +CONFIG_IP_SET_HASH_MAC=m +CONFIG_IP_SET_HASH_NETPORTNET=m +CONFIG_IP_SET_HASH_NET=m +CONFIG_IP_SET_HASH_NETNET=m +CONFIG_IP_SET_HASH_NETPORT=m +CONFIG_IP_SET_HASH_NETIFACE=m +CONFIG_IP_SET_LIST_SET=m +CONFIG_IP_VS=m +CONFIG_IP_VS_IPV6=y +CONFIG_IP_VS_PROTO_TCP=y +CONFIG_IP_VS_PROTO_UDP=y +CONFIG_IP_VS_PROTO_ESP=y +CONFIG_IP_VS_PROTO_AH=y +CONFIG_IP_VS_PROTO_SCTP=y +CONFIG_IP_VS_RR=m +CONFIG_IP_VS_WRR=m +CONFIG_IP_VS_LC=m +CONFIG_IP_VS_WLC=m +CONFIG_IP_VS_FO=m +CONFIG_IP_VS_OVF=m +CONFIG_IP_VS_LBLC=m +CONFIG_IP_VS_LBLCR=m +CONFIG_IP_VS_DH=m +CONFIG_IP_VS_SH=m +CONFIG_IP_VS_MH=m +CONFIG_IP_VS_SED=m +CONFIG_IP_VS_NQ=m +CONFIG_IP_VS_FTP=m +CONFIG_IP_VS_PE_SIP=m +CONFIG_NFT_DUP_IPV4=m +CONFIG_NFT_FIB_IPV4=m +CONFIG_NF_TABLES_ARP=y +CONFIG_NF_FLOW_TABLE_IPV4=m +CONFIG_NF_LOG_ARP=m +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_AH=m +CONFIG_IP_NF_MATCH_ECN=m +CONFIG_IP_NF_MATCH_RPFILTER=m +CONFIG_IP_NF_MATCH_TTL=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_SYNPROXY=m +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_NETMAP=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_IP_NF_MANGLE=m +CONFIG_IP_NF_TARGET_CLUSTERIP=m +CONFIG_IP_NF_TARGET_ECN=m +CONFIG_IP_NF_TARGET_TTL=m +CONFIG_IP_NF_RAW=m +CONFIG_IP_NF_SECURITY=m +CONFIG_IP_NF_ARPTABLES=m +CONFIG_IP_NF_ARPFILTER=m +CONFIG_IP_NF_ARP_MANGLE=m +CONFIG_NFT_DUP_IPV6=m +CONFIG_NFT_FIB_IPV6=m +CONFIG_NF_FLOW_TABLE_IPV6=m +CONFIG_IP6_NF_IPTABLES=m +CONFIG_IP6_NF_MATCH_AH=m +CONFIG_IP6_NF_MATCH_EUI64=m +CONFIG_IP6_NF_MATCH_FRAG=m +CONFIG_IP6_NF_MATCH_OPTS=m +CONFIG_IP6_NF_MATCH_HL=m +CONFIG_IP6_NF_MATCH_IPV6HEADER=m +CONFIG_IP6_NF_MATCH_MH=m +CONFIG_IP6_NF_MATCH_RPFILTER=m +CONFIG_IP6_NF_MATCH_RT=m +CONFIG_IP6_NF_MATCH_SRH=m +CONFIG_IP6_NF_TARGET_HL=m +CONFIG_IP6_NF_FILTER=m +CONFIG_IP6_NF_TARGET_REJECT=m +CONFIG_IP6_NF_TARGET_SYNPROXY=m +CONFIG_IP6_NF_MANGLE=m +CONFIG_IP6_NF_RAW=m +CONFIG_IP6_NF_SECURITY=m +CONFIG_IP6_NF_NAT=m +CONFIG_IP6_NF_TARGET_MASQUERADE=m +CONFIG_IP6_NF_TARGET_NPT=m +CONFIG_NF_TABLES_BRIDGE=m +CONFIG_NFT_BRIDGE_META=m +CONFIG_NFT_BRIDGE_REJECT=m +CONFIG_NF_LOG_BRIDGE=m +CONFIG_NF_CONNTRACK_BRIDGE=m +CONFIG_BRIDGE_NF_EBTABLES=m +CONFIG_BRIDGE_EBT_BROUTE=m +CONFIG_BRIDGE_EBT_T_FILTER=m +CONFIG_BRIDGE_EBT_T_NAT=m +CONFIG_BRIDGE_EBT_802_3=m +CONFIG_BRIDGE_EBT_AMONG=m +CONFIG_BRIDGE_EBT_ARP=m +CONFIG_BRIDGE_EBT_IP=m +CONFIG_BRIDGE_EBT_IP6=m +CONFIG_BRIDGE_EBT_LIMIT=m +CONFIG_BRIDGE_EBT_MARK=m +CONFIG_BRIDGE_EBT_PKTTYPE=m +CONFIG_BRIDGE_EBT_STP=m +CONFIG_BRIDGE_EBT_VLAN=m +CONFIG_BRIDGE_EBT_ARPREPLY=m +CONFIG_BRIDGE_EBT_DNAT=m +CONFIG_BRIDGE_EBT_MARK_T=m +CONFIG_BRIDGE_EBT_REDIRECT=m +CONFIG_BRIDGE_EBT_SNAT=m +CONFIG_BRIDGE_EBT_LOG=m +CONFIG_BRIDGE_EBT_NFLOG=m +CONFIG_IP_DCCP=m +CONFIG_SCTP_DEFAULT_COOKIE_HMAC_SHA1=y +CONFIG_SCTP_COOKIE_HMAC_MD5=y +CONFIG_RDS=m +CONFIG_RDS_TCP=m +CONFIG_TIPC=m +CONFIG_ATM=m +CONFIG_ATM_CLIP=m +CONFIG_ATM_LANE=m +CONFIG_ATM_MPOA=m +CONFIG_ATM_BR2684=m +CONFIG_L2TP=m +CONFIG_L2TP_DEBUGFS=m +CONFIG_L2TP_V3=y +CONFIG_L2TP_IP=m +CONFIG_L2TP_ETH=m +CONFIG_BRIDGE=m +CONFIG_BRIDGE_VLAN_FILTERING=y +CONFIG_VLAN_8021Q=m +CONFIG_VLAN_8021Q_GVRP=y +CONFIG_VLAN_8021Q_MVRP=y +CONFIG_LLC2=m +CONFIG_ATALK=m +CONFIG_DEV_APPLETALK=m +CONFIG_IPDDP=m +CONFIG_IPDDP_ENCAP=y +CONFIG_PHONET=m +CONFIG_6LOWPAN=m +CONFIG_6LOWPAN_GHC_EXT_HDR_HOP=m +CONFIG_6LOWPAN_GHC_UDP=m +CONFIG_6LOWPAN_GHC_ICMPV6=m +CONFIG_6LOWPAN_GHC_EXT_HDR_DEST=m +CONFIG_6LOWPAN_GHC_EXT_HDR_FRAG=m +CONFIG_6LOWPAN_GHC_EXT_HDR_ROUTE=m +CONFIG_IEEE802154=m +CONFIG_IEEE802154_6LOWPAN=m +CONFIG_MAC802154=m +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CBQ=m +CONFIG_NET_SCH_HTB=m +CONFIG_NET_SCH_HFSC=m +CONFIG_NET_SCH_ATM=m +CONFIG_NET_SCH_PRIO=m +CONFIG_NET_SCH_MULTIQ=m +CONFIG_NET_SCH_RED=m +CONFIG_NET_SCH_SFB=m +CONFIG_NET_SCH_SFQ=m +CONFIG_NET_SCH_TEQL=m +CONFIG_NET_SCH_TBF=m +CONFIG_NET_SCH_CBS=m +CONFIG_NET_SCH_ETF=m +CONFIG_NET_SCH_TAPRIO=m +CONFIG_NET_SCH_GRED=m +CONFIG_NET_SCH_DSMARK=m +CONFIG_NET_SCH_NETEM=m +CONFIG_NET_SCH_DRR=m +CONFIG_NET_SCH_MQPRIO=m +CONFIG_NET_SCH_SKBPRIO=m +CONFIG_NET_SCH_CHOKE=m +CONFIG_NET_SCH_QFQ=m +CONFIG_NET_SCH_CODEL=m +CONFIG_NET_SCH_FQ_CODEL=m +CONFIG_NET_SCH_CAKE=m +CONFIG_NET_SCH_FQ=m +CONFIG_NET_SCH_HHF=m +CONFIG_NET_SCH_PIE=m +CONFIG_NET_SCH_FQ_PIE=m +CONFIG_NET_SCH_INGRESS=m +CONFIG_NET_SCH_PLUG=m +CONFIG_NET_SCH_ETS=m +CONFIG_NET_CLS_BASIC=m +CONFIG_NET_CLS_TCINDEX=m +CONFIG_NET_CLS_ROUTE4=m +CONFIG_NET_CLS_FW=m +CONFIG_NET_CLS_U32=m +CONFIG_CLS_U32_PERF=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_RSVP=m +CONFIG_NET_CLS_RSVP6=m +CONFIG_NET_CLS_FLOW=m +CONFIG_NET_CLS_CGROUP=m +CONFIG_NET_CLS_BPF=m +CONFIG_NET_CLS_FLOWER=m +CONFIG_NET_CLS_MATCHALL=m +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_CMP=m +CONFIG_NET_EMATCH_NBYTE=m +CONFIG_NET_EMATCH_U32=m +CONFIG_NET_EMATCH_META=m +CONFIG_NET_EMATCH_TEXT=m +CONFIG_NET_EMATCH_CANID=m +CONFIG_NET_EMATCH_IPSET=m +CONFIG_NET_EMATCH_IPT=m +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=m +CONFIG_NET_ACT_GACT=m +CONFIG_GACT_PROB=y +CONFIG_NET_ACT_MIRRED=m +CONFIG_NET_ACT_SAMPLE=m +CONFIG_NET_ACT_IPT=m +CONFIG_NET_ACT_NAT=m +CONFIG_NET_ACT_PEDIT=m +CONFIG_NET_ACT_SIMP=m +CONFIG_NET_ACT_SKBEDIT=m +CONFIG_NET_ACT_CSUM=m +CONFIG_NET_ACT_MPLS=m +CONFIG_NET_ACT_VLAN=m +CONFIG_NET_ACT_BPF=m +CONFIG_NET_ACT_CONNMARK=m +CONFIG_NET_ACT_CTINFO=m +CONFIG_NET_ACT_SKBMOD=m +CONFIG_NET_ACT_IFE=m +CONFIG_NET_ACT_TUNNEL_KEY=m +CONFIG_NET_ACT_CT=m +CONFIG_NET_ACT_GATE=m +CONFIG_NET_IFE_SKBMARK=m +CONFIG_NET_IFE_SKBPRIO=m +CONFIG_NET_IFE_SKBTCINDEX=m +CONFIG_DCB=y +CONFIG_BATMAN_ADV=m +# CONFIG_BATMAN_ADV_BATMAN_V is not set +CONFIG_BATMAN_ADV_NC=y +CONFIG_BATMAN_ADV_DEBUGFS=y +CONFIG_OPENVSWITCH=m +CONFIG_VSOCKETS=m +CONFIG_VIRTIO_VSOCKETS=m +CONFIG_NETLINK_DIAG=m +CONFIG_NET_MPLS_GSO=y +CONFIG_MPLS_ROUTING=m +CONFIG_MPLS_IPTUNNEL=m +CONFIG_HSR=m +CONFIG_NET_SWITCHDEV=y +CONFIG_QRTR=m +CONFIG_QRTR_SMD=m +CONFIG_QRTR_TUN=m +CONFIG_NET_NCSI=y +CONFIG_CGROUP_NET_PRIO=y +CONFIG_BPF_JIT=y +CONFIG_BPF_STREAM_PARSER=y +CONFIG_NET_PKTGEN=m +CONFIG_NET_DROP_MONITOR=y +CONFIG_HAMRADIO=y +CONFIG_AX25=m +CONFIG_NETROM=m +CONFIG_ROSE=m +CONFIG_MKISS=m +CONFIG_6PACK=m +CONFIG_BPQETHER=m +CONFIG_BAYCOM_SER_FDX=m +CONFIG_BAYCOM_SER_HDX=m +CONFIG_YAM=m +CONFIG_CAN=m +CONFIG_CAN_J1939=m +CONFIG_CAN_ISOTP=m +CONFIG_CAN_VCAN=m +CONFIG_CAN_VXCAN=m +CONFIG_CAN_SLCAN=m +CONFIG_CAN_C_CAN=m +CONFIG_CAN_C_CAN_PLATFORM=m +CONFIG_CAN_HI311X=m +CONFIG_CAN_MCP251X=m +CONFIG_CAN_MCP251XFD=m +CONFIG_CAN_8DEV_USB=m +CONFIG_CAN_EMS_USB=m +CONFIG_CAN_ESD_USB2=m +CONFIG_CAN_GS_USB=m +CONFIG_CAN_KVASER_USB=m +CONFIG_CAN_MCBA_USB=m +CONFIG_CAN_PEAK_USB=m +CONFIG_CAN_UCAN=m +CONFIG_BT=m +CONFIG_BT_RFCOMM=m +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=m +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=m +CONFIG_BT_HS=y +CONFIG_BT_6LOWPAN=m +CONFIG_BT_LEDS=y +CONFIG_BT_MSFTEXT=y +CONFIG_BT_HCIBTUSB=m +CONFIG_BT_HCIBTUSB_MTK=y +CONFIG_BT_HCIBTSDIO=m +CONFIG_BT_HCIUART=m +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_ATH3K=y +CONFIG_BT_HCIUART_LL=y +CONFIG_BT_HCIUART_INTEL=y +CONFIG_BT_HCIUART_BCM=y +CONFIG_BT_HCIUART_RTL=y +CONFIG_BT_HCIUART_QCA=y +CONFIG_BT_HCIUART_AG6XX=y +CONFIG_BT_HCIUART_MRVL=y +CONFIG_BT_HCIBCM203X=m +CONFIG_BT_HCIBPA10X=m +CONFIG_BT_HCIBFUSB=m +CONFIG_BT_HCIVHCI=m +CONFIG_BT_MRVL=m +CONFIG_BT_MRVL_SDIO=m +CONFIG_BT_ATH3K=m +CONFIG_BT_MTKSDIO=m +CONFIG_BT_MTKUART=m +CONFIG_AF_RXRPC_IPV6=y +CONFIG_RXKAD=y +CONFIG_CFG80211=m +# CONFIG_CFG80211_DEFAULT_PS is not set +CONFIG_CFG80211_WEXT=y +CONFIG_MAC80211=m +CONFIG_MAC80211_MESH=y +CONFIG_RFKILL=y +CONFIG_RFKILL_INPUT=y +CONFIG_NET_9P=m +CONFIG_NET_9P_VIRTIO=m +CONFIG_RPMSG_PROTO=m +CONFIG_NFC=m +CONFIG_NFC_DIGITAL=m +CONFIG_NFC_NCI=m +CONFIG_NFC_NCI_SPI=m +CONFIG_NFC_NCI_UART=m +CONFIG_NFC_TRF7970A=m +CONFIG_NFC_SIM=m +CONFIG_NFC_PORT100=m +CONFIG_NFC_PN533_USB=m +CONFIG_NFC_PN533_I2C=m +CONFIG_NFC_ST_NCI_I2C=m +CONFIG_NFC_ST_NCI_SPI=m +CONFIG_NFC_NXP_NCI=m +CONFIG_NFC_NXP_NCI_I2C=m +CONFIG_NFC_ST95HF=m +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_EXTRA_FIRMWARE="regulatory.db regulatory.db.p7s am335x-pm-firmware.elf am335x-bone-scale-data.bin am335x-evm-scale-data.bin am43x-evm-scale-data.bin" +CONFIG_EXTRA_FIRMWARE_DIR="firmware" +CONFIG_OMAP_OCP2SCP=y +CONFIG_SIMPLE_PM_BUS=y +CONFIG_CONNECTOR=y +CONFIG_GNSS=m +CONFIG_GNSS_MTK_SERIAL=m +CONFIG_GNSS_SIRF_SERIAL=m +CONFIG_GNSS_UBX_SERIAL=m +CONFIG_MTD=y +CONFIG_MTD_AR7_PARTS=m +CONFIG_MTD_OF_PARTS=m +CONFIG_MTD_BLOCK=m +CONFIG_MTD_BLOCK_RO=m +CONFIG_RFD_FTL=m +CONFIG_SSFDC=m +CONFIG_MTD_OOPS=m +CONFIG_MTD_SWAP=m +CONFIG_MTD_PHYSMAP=m +CONFIG_MTD_PLATRAM=m +CONFIG_MTD_DATAFLASH=m +CONFIG_MTD_SST25L=m +CONFIG_MTD_ONENAND=y +CONFIG_MTD_ONENAND_VERIFY_WRITE=y +CONFIG_MTD_ONENAND_2X_PROGRAM=y +CONFIG_MTD_RAW_NAND=y +CONFIG_MTD_NAND_ECC_SW_BCH=y +CONFIG_MTD_NAND_OMAP2=m +CONFIG_MTD_NAND_NANDSIM=m +CONFIG_MTD_LPDDR=m +CONFIG_MTD_SPI_NOR=m +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_BLOCK=y +CONFIG_OF_OVERLAY=y +CONFIG_OF_CONFIGFS=y +CONFIG_BLK_DEV_NULL_BLK=m +CONFIG_ZRAM=m +CONFIG_ZRAM_WRITEBACK=y +CONFIG_ZRAM_MEMORY_TRACKING=y +CONFIG_BLK_DEV_LOOP=m +CONFIG_BLK_DEV_DRBD=m +CONFIG_BLK_DEV_NBD=m +CONFIG_BLK_DEV_RAM=m +CONFIG_BLK_DEV_RAM_SIZE=16384 +CONFIG_ATA_OVER_ETH=m +CONFIG_VIRTIO_BLK=m +CONFIG_BLK_DEV_RBD=m +CONFIG_AD525X_DPOT=m +CONFIG_AD525X_DPOT_I2C=m +CONFIG_AD525X_DPOT_SPI=m +CONFIG_ICS932S401=m +CONFIG_APDS9802ALS=m +CONFIG_ISL29003=m +CONFIG_ISL29020=m +CONFIG_SENSORS_TSL2550=m +CONFIG_SENSORS_BH1770=m +CONFIG_SENSORS_APDS990X=m +CONFIG_HMC6352=m +CONFIG_DS1682=m +CONFIG_SRAM=y +CONFIG_DMA_BUF_PHYS=y +CONFIG_EEPROM_AT24=y +CONFIG_EEPROM_AT25=y +CONFIG_EEPROM_LEGACY=m +CONFIG_EEPROM_MAX6875=m +CONFIG_EEPROM_93XX46=m +CONFIG_TI_ST=m +CONFIG_BEAGLEBONE_PINMUX_HELPER=y +CONFIG_RAID_ATTRS=m +# CONFIG_SCSI_PROC_FS is not set +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_ST=m +CONFIG_BLK_DEV_SR=m +CONFIG_CHR_DEV_SG=m +CONFIG_CHR_DEV_SCH=m +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +CONFIG_SCSI_SPI_ATTRS=m +CONFIG_SCSI_FC_ATTRS=m +CONFIG_SCSI_ISCSI_ATTRS=m +CONFIG_SCSI_SAS_LIBSAS=m +CONFIG_SCSI_SAS_ATA=y +CONFIG_SCSI_SRP_ATTRS=m +CONFIG_ATA=y +CONFIG_SATA_AHCI_PLATFORM=y +CONFIG_MD=y +CONFIG_BLK_DEV_MD=m +CONFIG_MD_LINEAR=m +CONFIG_MD_RAID0=m +CONFIG_MD_RAID1=m +CONFIG_MD_RAID10=m +CONFIG_MD_MULTIPATH=m +CONFIG_MD_FAULTY=m +CONFIG_MD_CLUSTER=m +CONFIG_BCACHE=m +CONFIG_BLK_DEV_DM=m +CONFIG_DM_UNSTRIPED=m +CONFIG_DM_CRYPT=m +CONFIG_DM_SNAPSHOT=m +CONFIG_DM_THIN_PROVISIONING=m +CONFIG_DM_CACHE=m +CONFIG_DM_WRITECACHE=m +CONFIG_DM_ERA=m +CONFIG_DM_MIRROR=m +CONFIG_DM_LOG_USERSPACE=m +CONFIG_DM_ZERO=m +CONFIG_DM_MULTIPATH=m +CONFIG_DM_MULTIPATH_QL=m +CONFIG_DM_MULTIPATH_ST=m +CONFIG_DM_DELAY=m +CONFIG_DM_UEVENT=y +CONFIG_DM_FLAKEY=m +CONFIG_DM_VERITY=m +CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG=y +CONFIG_DM_SWITCH=m +CONFIG_DM_LOG_WRITES=m +CONFIG_DM_INTEGRITY=m +CONFIG_DM_ZONED=m +CONFIG_TARGET_CORE=m +CONFIG_TCM_IBLOCK=m +CONFIG_TCM_FILEIO=m +CONFIG_TCM_PSCSI=m +CONFIG_TCM_USER2=m +CONFIG_LOOPBACK_TARGET=m +CONFIG_ISCSI_TARGET=m +CONFIG_NETDEVICES=y +CONFIG_BONDING=m +CONFIG_DUMMY=m +CONFIG_WIREGUARD=m +CONFIG_EQUALIZER=m +CONFIG_IFB=m +CONFIG_NET_TEAM=m +CONFIG_NET_TEAM_MODE_BROADCAST=m +CONFIG_NET_TEAM_MODE_ROUNDROBIN=m +CONFIG_NET_TEAM_MODE_RANDOM=m +CONFIG_NET_TEAM_MODE_ACTIVEBACKUP=m +CONFIG_NET_TEAM_MODE_LOADBALANCE=m +CONFIG_MACVLAN=m +CONFIG_MACVTAP=m +CONFIG_IPVLAN=m +CONFIG_IPVTAP=m +CONFIG_VXLAN=m +CONFIG_GENEVE=m +CONFIG_GTP=m +CONFIG_MACSEC=m +CONFIG_NETCONSOLE=m +CONFIG_NETCONSOLE_DYNAMIC=y +CONFIG_TUN=m +CONFIG_VETH=m +CONFIG_VIRTIO_NET=m +CONFIG_NLMON=m +CONFIG_NET_VRF=m +CONFIG_ATM_DUMMY=m +# CONFIG_NET_VENDOR_ALACRITECH is not set +# CONFIG_NET_VENDOR_AMAZON is not set +# CONFIG_NET_VENDOR_AQUANTIA is not set +# CONFIG_NET_VENDOR_ARC is not set +# CONFIG_NET_VENDOR_AURORA is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_VENDOR_CADENCE is not set +# CONFIG_NET_VENDOR_CAVIUM is not set +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_NET_VENDOR_CORTINA is not set +# CONFIG_NET_VENDOR_EZCHIP is not set +# CONFIG_NET_VENDOR_FARADAY is not set +# CONFIG_NET_VENDOR_GOOGLE is not set +# CONFIG_NET_VENDOR_HISILICON is not set +# CONFIG_NET_VENDOR_HUAWEI is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MELLANOX is not set +CONFIG_KS8851=m +CONFIG_ENC28J60=y +CONFIG_ENCX24J600=y +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_NETRONOME is not set +# CONFIG_NET_VENDOR_NI is not set +# CONFIG_NET_VENDOR_PENSANDO is not set +# CONFIG_NET_VENDOR_QUALCOMM is not set +# CONFIG_NET_VENDOR_RENESAS is not set +# CONFIG_NET_VENDOR_ROCKER is not set +# CONFIG_NET_VENDOR_SAMSUNG is not set +# CONFIG_NET_VENDOR_SEEQ is not set +# CONFIG_NET_VENDOR_SOLARFLARE is not set +CONFIG_SMC91X=m +CONFIG_SMC911X=m +CONFIG_SMSC911X=m +# CONFIG_NET_VENDOR_SOCIONEXT is not set +# CONFIG_NET_VENDOR_STMICRO is not set +# CONFIG_NET_VENDOR_SYNOPSYS is not set +CONFIG_TI_CPSW_PHY_SEL=y +CONFIG_TI_CPSW=y +CONFIG_TI_CPSW_SWITCHDEV=y +CONFIG_TI_CPTS=y +# CONFIG_TI_ICSS_IEP is not set +# CONFIG_NET_VENDOR_VIA is not set +CONFIG_WIZNET_W5100=y +CONFIG_WIZNET_W5100_SPI=y +# CONFIG_NET_VENDOR_XILINX is not set +CONFIG_LED_TRIGGER_PHY=y +CONFIG_MICREL_PHY=y +CONFIG_MICROCHIP_PHY=y +CONFIG_MICROSEMI_PHY=m +CONFIG_AT803X_PHY=y +CONFIG_DP83867_PHY=y +CONFIG_DP83869_PHY=m +CONFIG_VITESSE_PHY=y +CONFIG_MDIO_BCM_UNIMAC=m +CONFIG_MDIO_GPIO=y +CONFIG_PPP=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPP_FILTER=y +CONFIG_PPP_MPPE=m +CONFIG_PPP_MULTILINK=y +CONFIG_PPPOATM=m +CONFIG_PPPOE=m +CONFIG_PPTP=m +CONFIG_PPPOL2TP=m +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m +CONFIG_SLIP=m +CONFIG_SLIP_COMPRESSED=y +CONFIG_SLIP_SMART=y +CONFIG_SLIP_MODE_SLIP6=y +CONFIG_USB_CATC=m +CONFIG_USB_KAWETH=m +CONFIG_USB_PEGASUS=m +CONFIG_USB_RTL8150=m +CONFIG_USB_RTL8152=m +CONFIG_USB_LAN78XX=m +CONFIG_USB_USBNET=y +CONFIG_USB_NET_AX8817X=m +CONFIG_USB_NET_AX88179_178A=m +CONFIG_USB_NET_CDCETHER=m +CONFIG_USB_NET_CDC_EEM=m +CONFIG_USB_NET_CDC_NCM=m +CONFIG_USB_NET_HUAWEI_CDC_NCM=m +CONFIG_USB_NET_CDC_MBIM=m +CONFIG_USB_NET_DM9601=m +CONFIG_USB_NET_SR9700=m +CONFIG_USB_NET_SR9800=m +CONFIG_USB_NET_SMSC75XX=m +CONFIG_USB_NET_SMSC95XX=y +CONFIG_USB_NET_GL620A=m +CONFIG_USB_NET_NET1080=m +CONFIG_USB_NET_PLUSB=m +CONFIG_USB_NET_MCS7830=m +CONFIG_USB_NET_CDC_SUBSET=m +CONFIG_USB_ALI_M5632=y +CONFIG_USB_AN2720=y +CONFIG_USB_EPSON2888=y +CONFIG_USB_KC2190=y +CONFIG_USB_NET_ZAURUS=m +CONFIG_USB_NET_CX82310_ETH=m +CONFIG_USB_NET_KALMIA=m +CONFIG_USB_NET_QMI_WWAN=m +CONFIG_USB_HSO=m +CONFIG_USB_NET_INT51X1=m +CONFIG_USB_CDC_PHONET=m +CONFIG_USB_IPHETH=m +CONFIG_USB_SIERRA_NET=m +CONFIG_USB_VL600=m +CONFIG_USB_NET_CH9200=m +CONFIG_USB_NET_AQC111=m +CONFIG_ATH9K=m +CONFIG_ATH9K_CHANNEL_CONTEXT=y +CONFIG_ATH9K_HTC=m +CONFIG_ATH9K_HWRNG=y +CONFIG_CARL9170=m +CONFIG_ATH6KL=m +CONFIG_ATH6KL_SDIO=m +CONFIG_ATH6KL_USB=m +CONFIG_AR5523=m +CONFIG_ATH10K=m +CONFIG_ATH10K_USB=m +CONFIG_ATH11K=m +CONFIG_ATH11K_AHB=m +CONFIG_AT76C50X_USB=m +CONFIG_BRCMFMAC=m +CONFIG_BRCMFMAC_USB=y +CONFIG_HOSTAP=m +CONFIG_HOSTAP_FIRMWARE=y +CONFIG_P54_COMMON=m +CONFIG_P54_USB=m +CONFIG_LIBERTAS=m +CONFIG_LIBERTAS_USB=m +CONFIG_LIBERTAS_SDIO=m +CONFIG_LIBERTAS_MESH=y +CONFIG_LIBERTAS_THINFIRM=m +CONFIG_LIBERTAS_THINFIRM_USB=m +CONFIG_MT7601U=m +CONFIG_MT76x0U=m +CONFIG_MT76x2U=m +CONFIG_WILC1000_SDIO=m +CONFIG_WILC1000_SPI=m +CONFIG_RT2X00=m +CONFIG_RT2500USB=m +CONFIG_RT73USB=m +CONFIG_RT2800USB=m +CONFIG_RT2800USB_RT3573=y +CONFIG_RT2800USB_RT53XX=y +CONFIG_RT2800USB_RT55XX=y +CONFIG_RTL8187=m +CONFIG_RTL8192CU=m +# CONFIG_RTLWIFI_DEBUG is not set +CONFIG_RTL8XXXU=m +CONFIG_RTW88=m +CONFIG_RSI_91X=m +# CONFIG_RSI_SDIO is not set +CONFIG_WL1251=m +CONFIG_WL1251_SPI=m +CONFIG_WL1251_SDIO=m +CONFIG_WL12XX=m +CONFIG_WL18XX=m +CONFIG_WLCORE_SPI=m +CONFIG_WLCORE_SDIO=m +CONFIG_USB_ZD1201=m +CONFIG_ZD1211RW=m +CONFIG_MAC80211_HWSIM=m +CONFIG_USB_NET_RNDIS_WLAN=m +CONFIG_IEEE802154_AT86RF230=m +CONFIG_IEEE802154_MRF24J40=m +CONFIG_IEEE802154_CC2520=m +CONFIG_IEEE802154_ATUSB=m +CONFIG_IEEE802154_WPANUSB=m +CONFIG_IEEE802154_ADF7242=m +CONFIG_IEEE802154_CA8210=m +CONFIG_IEEE802154_MCR20A=m +CONFIG_IEEE802154_HWSIM=m +CONFIG_IEEE802154_BCFSERIAL=m +CONFIG_INPUT_POLLDEV=y +CONFIG_INPUT_SPARSEKMAP=m +CONFIG_INPUT_JOYDEV=m +CONFIG_INPUT_EVDEV=m +CONFIG_KEYBOARD_ADP5588=m +# CONFIG_KEYBOARD_ATKBD is not set +CONFIG_KEYBOARD_QT2160=m +CONFIG_KEYBOARD_GPIO=y +CONFIG_KEYBOARD_TCA8418=m +CONFIG_KEYBOARD_LM8323=m +CONFIG_KEYBOARD_MAX7359=m +CONFIG_KEYBOARD_STOWAWAY=m +# CONFIG_MOUSE_PS2 is not set +CONFIG_MOUSE_APPLETOUCH=m +CONFIG_MOUSE_ELAN_I2C=m +CONFIG_MOUSE_SYNAPTICS_I2C=m +CONFIG_MOUSE_SYNAPTICS_USB=m +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_IFORCE=m +CONFIG_JOYSTICK_IFORCE_USB=m +CONFIG_JOYSTICK_IFORCE_232=m +CONFIG_JOYSTICK_WARRIOR=m +CONFIG_JOYSTICK_MAGELLAN=m +CONFIG_JOYSTICK_SPACEORB=m +CONFIG_JOYSTICK_SPACEBALL=m +CONFIG_JOYSTICK_STINGER=m +CONFIG_JOYSTICK_TWIDJOY=m +CONFIG_JOYSTICK_ZHENHUA=m +CONFIG_JOYSTICK_AS5011=m +CONFIG_JOYSTICK_XPAD=m +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y +CONFIG_JOYSTICK_PSXPAD_SPI=y +CONFIG_JOYSTICK_PSXPAD_SPI_FF=y +CONFIG_JOYSTICK_PXRC=m +CONFIG_JOYSTICK_FSIA6B=m +CONFIG_INPUT_TABLET=y +CONFIG_TABLET_USB_ACECAD=m +CONFIG_TABLET_USB_AIPTEK=m +CONFIG_TABLET_USB_GTCO=m +CONFIG_TABLET_USB_HANWANG=m +CONFIG_TABLET_USB_KBTAB=m +CONFIG_TABLET_USB_PEGASUS=m +CONFIG_TABLET_SERIAL_WACOM4=m +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ADS7846=m +CONFIG_TOUCHSCREEN_AD7877=m +CONFIG_TOUCHSCREEN_AD7879=m +CONFIG_TOUCHSCREEN_AD7879_I2C=m +CONFIG_TOUCHSCREEN_AR1021_I2C=y +CONFIG_TOUCHSCREEN_ATMEL_MXT=m +CONFIG_TOUCHSCREEN_DYNAPRO=m +CONFIG_TOUCHSCREEN_HAMPSHIRE=m +CONFIG_TOUCHSCREEN_FUJITSU=m +CONFIG_TOUCHSCREEN_GOODIX=m +CONFIG_TOUCHSCREEN_GUNZE=m +CONFIG_TOUCHSCREEN_ELO=m +CONFIG_TOUCHSCREEN_WACOM_W8001=m +CONFIG_TOUCHSCREEN_MCS5000=m +CONFIG_TOUCHSCREEN_MTOUCH=m +CONFIG_TOUCHSCREEN_INEXIO=m +CONFIG_TOUCHSCREEN_MK712=m +CONFIG_TOUCHSCREEN_PENMOUNT=m +CONFIG_TOUCHSCREEN_EDT_FT5X06=y +CONFIG_TOUCHSCREEN_TOUCHRIGHT=m +CONFIG_TOUCHSCREEN_TOUCHWIN=m +CONFIG_TOUCHSCREEN_TI_AM335X_TSC=y +CONFIG_TOUCHSCREEN_USB_COMPOSITE=m +CONFIG_TOUCHSCREEN_TSC2005=m +CONFIG_TOUCHSCREEN_TSC2007=m +CONFIG_TOUCHSCREEN_SILEAD=y +CONFIG_TOUCHSCREEN_STMPE=y +CONFIG_TOUCHSCREEN_TPS6507X=m +CONFIG_INPUT_MISC=y +CONFIG_INPUT_AD714X=m +CONFIG_INPUT_MMA8450=m +CONFIG_INPUT_GPIO_DECODER=m +CONFIG_INPUT_ATI_REMOTE2=m +CONFIG_INPUT_KEYSPAN_REMOTE=m +CONFIG_INPUT_POWERMATE=m +CONFIG_INPUT_YEALINK=m +CONFIG_INPUT_CM109=m +CONFIG_INPUT_TPS65218_PWRBUTTON=y +CONFIG_INPUT_TPS65219_PWRBUTTON=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_PALMAS_PWRBUTTON=y +CONFIG_RMI4_F34=y +CONFIG_RMI4_F55=y +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_8250=y +# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set +# CONFIG_SERIAL_8250_16550A_VARIANTS is not set +CONFIG_SERIAL_8250_CONSOLE=y +# CONFIG_SERIAL_8250_DMA is not set +CONFIG_SERIAL_8250_NR_UARTS=6 +CONFIG_SERIAL_8250_RUNTIME_UARTS=6 +CONFIG_SERIAL_8250_OMAP=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_MAX3100=m +CONFIG_SERIAL_MAX310X=m +CONFIG_N_GSM=m +CONFIG_SERIAL_DEV_BUS=y +CONFIG_TTY_PRINTK=m +CONFIG_VIRTIO_CONSOLE=m +CONFIG_HW_RANDOM_VIRTIO=m +CONFIG_TCG_TPM=y +CONFIG_TCG_TIS_I2C_ATMEL=y +CONFIG_TCG_TIS_I2C_INFINEON=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_ARB_GPIO_CHALLENGE=m +CONFIG_I2C_MUX_GPIO=y +CONFIG_I2C_MUX_PCA954x=m +CONFIG_I2C_MUX_PINCTRL=y +CONFIG_I2C_GPIO=y +CONFIG_I2C_DIOLAN_U2C=m +CONFIG_I2C_ROBOTFUZZ_OSIF=m +CONFIG_I2C_TAOS_EVM=m +CONFIG_I2C_TINY_USB=m +CONFIG_I2C_SLAVE=y +CONFIG_I2C_SLAVE_EEPROM=y +CONFIG_SPI=y +CONFIG_SPI_GPIO=y +CONFIG_SPI_OMAP24XX=y +CONFIG_SPI_TI_QSPI=y +CONFIG_SPI_SPIDEV=m +CONFIG_SPI_SLAVE=y +CONFIG_SPI_SLAVE_TIME=m +CONFIG_SPI_SLAVE_SYSTEM_CONTROL=m +CONFIG_PPS_CLIENT_LDISC=m +CONFIG_PPS_CLIENT_GPIO=m +CONFIG_PTP_1588_CLOCK=y +CONFIG_PINCTRL_MCP23S08=m +CONFIG_PINCTRL_SINGLE=y +CONFIG_PINCTRL_PALMAS=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_OF_HELPER=y +CONFIG_GPIO_GENERIC_PLATFORM=y +CONFIG_GPIO_SYSCON=y +CONFIG_GPIO_ADP5588=m +CONFIG_GPIO_ADNP=m +CONFIG_GPIO_MAX7300=m +CONFIG_GPIO_MAX732X=m +CONFIG_GPIO_PCA953X=y +CONFIG_GPIO_PCA953X_IRQ=y +CONFIG_GPIO_PCF857X=m +CONFIG_GPIO_TPIC2810=m +CONFIG_GPIO_PALMAS=y +CONFIG_GPIO_STMPE=y +CONFIG_GPIO_TPS65218=y +CONFIG_GPIO_74X164=m +CONFIG_GPIO_MAX3191X=m +CONFIG_GPIO_MAX7301=m +CONFIG_GPIO_MC33880=m +CONFIG_GPIO_PISOSR=m +CONFIG_GPIO_XRA1403=m +CONFIG_GPIO_AGGREGATOR=m +CONFIG_W1=m +CONFIG_W1_MASTER_DS2490=m +CONFIG_W1_MASTER_DS2482=m +CONFIG_W1_MASTER_GPIO=m +CONFIG_W1_SLAVE_THERM=m +CONFIG_W1_SLAVE_SMEM=m +CONFIG_W1_SLAVE_DS2405=m +CONFIG_W1_SLAVE_DS2408=m +CONFIG_W1_SLAVE_DS2413=m +CONFIG_W1_SLAVE_DS2406=m +CONFIG_W1_SLAVE_DS2423=m +CONFIG_W1_SLAVE_DS2805=m +CONFIG_W1_SLAVE_DS2430=m +CONFIG_W1_SLAVE_DS2431=m +CONFIG_W1_SLAVE_DS2433=m +CONFIG_W1_SLAVE_DS2433_CRC=y +CONFIG_W1_SLAVE_DS2438=m +CONFIG_W1_SLAVE_DS250X=m +CONFIG_W1_SLAVE_DS2780=m +CONFIG_W1_SLAVE_DS2781=m +CONFIG_W1_SLAVE_DS28E04=m +CONFIG_W1_SLAVE_DS28E17=m +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_GPIO=y +CONFIG_POWER_RESET_GPIO_RESTART=y +CONFIG_POWER_RESET_RESTART=y +CONFIG_POWER_RESET_SYSCON=y +CONFIG_POWER_RESET_SYSCON_POWEROFF=y +CONFIG_GENERIC_ADC_BATTERY=m +CONFIG_BATTERY_DS2760=m +CONFIG_CHARGER_GPIO=m +CONFIG_SENSORS_AD7314=m +CONFIG_SENSORS_AD7414=m +CONFIG_SENSORS_AD7418=m +CONFIG_SENSORS_ADM1021=m +CONFIG_SENSORS_ADM1025=m +CONFIG_SENSORS_ADM1026=m +CONFIG_SENSORS_ADM1029=m +CONFIG_SENSORS_ADM1031=m +CONFIG_SENSORS_ADM1177=m +CONFIG_SENSORS_ADM9240=m +CONFIG_SENSORS_ADT7310=m +CONFIG_SENSORS_ADT7410=m +CONFIG_SENSORS_ADT7411=m +CONFIG_SENSORS_ADT7462=m +CONFIG_SENSORS_ADT7470=m +CONFIG_SENSORS_ADT7475=m +CONFIG_SENSORS_AS370=m +CONFIG_SENSORS_ASC7621=m +CONFIG_SENSORS_ASPEED=m +CONFIG_SENSORS_ATXP1=m +CONFIG_SENSORS_DRIVETEMP=m +CONFIG_SENSORS_DS620=m +CONFIG_SENSORS_DS1621=m +CONFIG_SENSORS_F71805F=m +CONFIG_SENSORS_F71882FG=m +CONFIG_SENSORS_F75375S=m +CONFIG_SENSORS_FTSTEUTATES=m +CONFIG_SENSORS_GL518SM=m +CONFIG_SENSORS_GL520SM=m +CONFIG_SENSORS_G760A=m +CONFIG_SENSORS_G762=m +CONFIG_SENSORS_GPIO_FAN=y +CONFIG_SENSORS_HIH6130=m +CONFIG_SENSORS_IIO_HWMON=m +CONFIG_SENSORS_IT87=m +CONFIG_SENSORS_JC42=m +CONFIG_SENSORS_POWR1220=m +CONFIG_SENSORS_LINEAGE=m +CONFIG_SENSORS_LTC2945=m +CONFIG_SENSORS_LTC2990=m +CONFIG_SENSORS_LTC4151=m +CONFIG_SENSORS_LTC4215=m +CONFIG_SENSORS_LTC4222=m +CONFIG_SENSORS_LTC4245=m +CONFIG_SENSORS_LTC4260=m +CONFIG_SENSORS_LTC4261=m +CONFIG_SENSORS_MAX1111=m +CONFIG_SENSORS_MAX16065=m +CONFIG_SENSORS_MAX1619=m +CONFIG_SENSORS_MAX1668=m +CONFIG_SENSORS_MAX197=m +CONFIG_SENSORS_MAX31722=m +CONFIG_SENSORS_MAX31730=m +CONFIG_SENSORS_MAX6621=m +CONFIG_SENSORS_MAX6639=m +CONFIG_SENSORS_MAX6642=m +CONFIG_SENSORS_MAX6650=m +CONFIG_SENSORS_MAX6697=m +CONFIG_SENSORS_MAX31790=m +CONFIG_SENSORS_MCP3021=m +CONFIG_SENSORS_TC654=m +CONFIG_SENSORS_MR75203=m +CONFIG_SENSORS_ADCXX=m +CONFIG_SENSORS_LM63=m +CONFIG_SENSORS_LM70=m +CONFIG_SENSORS_LM73=m +CONFIG_SENSORS_LM75=m +CONFIG_SENSORS_LM77=m +CONFIG_SENSORS_LM78=m +CONFIG_SENSORS_LM80=m +CONFIG_SENSORS_LM83=m +CONFIG_SENSORS_LM85=m +CONFIG_SENSORS_LM87=m +CONFIG_SENSORS_LM90=m +CONFIG_SENSORS_LM92=m +CONFIG_SENSORS_LM93=m +CONFIG_SENSORS_LM95234=m +CONFIG_SENSORS_LM95241=m +CONFIG_SENSORS_LM95245=m +CONFIG_SENSORS_PC87360=m +CONFIG_SENSORS_PC87427=m +CONFIG_SENSORS_NTC_THERMISTOR=m +CONFIG_SENSORS_NCT6683=m +CONFIG_SENSORS_NCT6775=m +CONFIG_SENSORS_NCT7802=m +CONFIG_SENSORS_NCT7904=m +CONFIG_SENSORS_NPCM7XX=m +CONFIG_SENSORS_PCF8591=m +CONFIG_PMBUS=m +CONFIG_SENSORS_ADM1266=m +CONFIG_SENSORS_ADM1275=m +CONFIG_SENSORS_IBM_CFFPS=m +CONFIG_SENSORS_INSPUR_IPSPS=m +CONFIG_SENSORS_IR35221=m +CONFIG_SENSORS_IR38064=m +CONFIG_SENSORS_IRPS5401=m +CONFIG_SENSORS_ISL68137=m +CONFIG_SENSORS_LM25066=m +CONFIG_SENSORS_LTC2978=m +CONFIG_SENSORS_LTC2978_REGULATOR=y +CONFIG_SENSORS_LTC3815=m +CONFIG_SENSORS_MAX16064=m +CONFIG_SENSORS_MAX20730=m +CONFIG_SENSORS_MAX20751=m +CONFIG_SENSORS_MAX31785=m +CONFIG_SENSORS_MAX34440=m +CONFIG_SENSORS_MAX8688=m +CONFIG_SENSORS_MP2975=m +CONFIG_SENSORS_PXE1610=m +CONFIG_SENSORS_TPS40422=m +CONFIG_SENSORS_TPS53679=m +CONFIG_SENSORS_UCD9000=m +CONFIG_SENSORS_UCD9200=m +CONFIG_SENSORS_XDPE122=m +CONFIG_SENSORS_ZL6100=m +CONFIG_SENSORS_PWM_FAN=m +CONFIG_SENSORS_SHT15=m +CONFIG_SENSORS_SHT21=m +CONFIG_SENSORS_SHT3x=m +CONFIG_SENSORS_SHTC1=m +CONFIG_SENSORS_DME1737=m +CONFIG_SENSORS_EMC1403=m +CONFIG_SENSORS_EMC2103=m +CONFIG_SENSORS_EMC6W201=m +CONFIG_SENSORS_SMSC47M1=m +CONFIG_SENSORS_SMSC47M192=m +CONFIG_SENSORS_SMSC47B397=m +CONFIG_SENSORS_SCH5627=m +CONFIG_SENSORS_SCH5636=m +CONFIG_SENSORS_STTS751=m +CONFIG_SENSORS_SMM665=m +CONFIG_SENSORS_ADC128D818=m +CONFIG_SENSORS_ADS7828=m +CONFIG_SENSORS_ADS7871=m +CONFIG_SENSORS_AMC6821=m +CONFIG_SENSORS_INA209=m +CONFIG_SENSORS_INA2XX=m +CONFIG_SENSORS_INA3221=m +CONFIG_SENSORS_TC74=m +CONFIG_SENSORS_THMC50=m +CONFIG_SENSORS_TMP102=m +CONFIG_SENSORS_TMP103=m +CONFIG_SENSORS_TMP108=m +CONFIG_SENSORS_TMP401=m +CONFIG_SENSORS_TMP421=m +CONFIG_SENSORS_VT1211=m +CONFIG_SENSORS_W83773G=m +CONFIG_SENSORS_W83781D=m +CONFIG_SENSORS_W83791D=m +CONFIG_SENSORS_W83792D=m +CONFIG_SENSORS_W83793=m +CONFIG_SENSORS_W83795=m +CONFIG_SENSORS_W83L785TS=m +CONFIG_SENSORS_W83L786NG=m +CONFIG_SENSORS_W83627HF=m +CONFIG_SENSORS_W83627EHF=m +CONFIG_THERMAL_STATISTICS=y +CONFIG_THERMAL_GOV_FAIR_SHARE=y +CONFIG_THERMAL_GOV_BANG_BANG=y +CONFIG_THERMAL_GOV_USER_SPACE=y +CONFIG_THERMAL_GOV_POWER_ALLOCATOR=y +CONFIG_CPU_THERMAL=y +CONFIG_DEVFREQ_THERMAL=y +CONFIG_THERMAL_EMULATION=y +CONFIG_TI_THERMAL=y +CONFIG_OMAP5_THERMAL=y +CONFIG_DRA752_THERMAL=y +CONFIG_GENERIC_ADC_THERMAL=m +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_SYSFS=y +CONFIG_WATCHDOG_PRETIMEOUT_GOV=y +CONFIG_WATCHDOG_PRETIMEOUT_GOV_PANIC=m +CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_NOOP=y +CONFIG_SOFT_WATCHDOG=y +CONFIG_OMAP_WATCHDOG=y +CONFIG_MFD_STMPE=y +CONFIG_MFD_TI_AM335X_TSCADC=y +CONFIG_MFD_PALMAS=y +CONFIG_MFD_TPS65217=y +CONFIG_MFD_TPS65218=y +CONFIG_MFD_TPS65219=y +CONFIG_MFD_WL1273_CORE=m +CONFIG_REGULATOR_USERSPACE_CONSUMER=y +CONFIG_REGULATOR_GPIO=y +CONFIG_REGULATOR_PALMAS=y +CONFIG_REGULATOR_PBIAS=y +CONFIG_REGULATOR_PWM=y +CONFIG_REGULATOR_TI_ABB=y +CONFIG_REGULATOR_TPS65217=y +CONFIG_REGULATOR_TPS65218=y +CONFIG_REGULATOR_TPS65219=y +CONFIG_RC_CORE=m +CONFIG_LIRC=y +CONFIG_RC_DECODERS=y +CONFIG_IR_NEC_DECODER=m +CONFIG_IR_RC5_DECODER=m +CONFIG_IR_RC6_DECODER=m +CONFIG_IR_JVC_DECODER=m +CONFIG_IR_SONY_DECODER=m +CONFIG_IR_SANYO_DECODER=m +CONFIG_IR_SHARP_DECODER=m +CONFIG_IR_MCE_KBD_DECODER=m +CONFIG_IR_XMP_DECODER=m +CONFIG_IR_IMON_DECODER=m +CONFIG_RC_DEVICES=y +CONFIG_RC_ATI_REMOTE=m +CONFIG_IR_IMON=m +CONFIG_IR_IMON_RAW=m +CONFIG_IR_MCEUSB=m +CONFIG_IR_REDRAT3=m +CONFIG_IR_STREAMZAP=m +CONFIG_IR_IGORPLUGUSB=m +CONFIG_IR_IGUANA=m +CONFIG_IR_TTUSBIR=m +CONFIG_RC_LOOPBACK=m +CONFIG_IR_GPIO_CIR=m +CONFIG_USB_PULSE8_CEC=m +CONFIG_USB_RAINSHADOW_CEC=m +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_SUBDRV_AUTOSELECT=y +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_USB_VIDEO_CLASS=m +CONFIG_USB_M5602=m +CONFIG_USB_STV06XX=m +CONFIG_USB_GL860=m +CONFIG_USB_GSPCA_BENQ=m +CONFIG_USB_GSPCA_CONEX=m +CONFIG_USB_GSPCA_CPIA1=m +CONFIG_USB_GSPCA_DTCS033=m +CONFIG_USB_GSPCA_ETOMS=m +CONFIG_USB_GSPCA_FINEPIX=m +CONFIG_USB_GSPCA_JEILINJ=m +CONFIG_USB_GSPCA_JL2005BCD=m +CONFIG_USB_GSPCA_KINECT=m +CONFIG_USB_GSPCA_KONICA=m +CONFIG_USB_GSPCA_MARS=m +CONFIG_USB_GSPCA_MR97310A=m +CONFIG_USB_GSPCA_NW80X=m +CONFIG_USB_GSPCA_OV519=m +CONFIG_USB_GSPCA_OV534=m +CONFIG_USB_GSPCA_OV534_9=m +CONFIG_USB_GSPCA_PAC207=m +CONFIG_USB_GSPCA_PAC7302=m +CONFIG_USB_GSPCA_PAC7311=m +CONFIG_USB_GSPCA_SE401=m +CONFIG_USB_GSPCA_SN9C2028=m +CONFIG_USB_GSPCA_SN9C20X=m +CONFIG_USB_GSPCA_SONIXB=m +CONFIG_USB_GSPCA_SONIXJ=m +CONFIG_USB_GSPCA_SPCA500=m +CONFIG_USB_GSPCA_SPCA501=m +CONFIG_USB_GSPCA_SPCA505=m +CONFIG_USB_GSPCA_SPCA506=m +CONFIG_USB_GSPCA_SPCA508=m +CONFIG_USB_GSPCA_SPCA561=m +CONFIG_USB_GSPCA_SPCA1528=m +CONFIG_USB_GSPCA_SQ905=m +CONFIG_USB_GSPCA_SQ905C=m +CONFIG_USB_GSPCA_SQ930X=m +CONFIG_USB_GSPCA_STK014=m +CONFIG_USB_GSPCA_STK1135=m +CONFIG_USB_GSPCA_STV0680=m +CONFIG_USB_GSPCA_SUNPLUS=m +CONFIG_USB_GSPCA_T613=m +CONFIG_USB_GSPCA_TOPRO=m +CONFIG_USB_GSPCA_TOUPTEK=m +CONFIG_USB_GSPCA_TV8532=m +CONFIG_USB_GSPCA_VC032X=m +CONFIG_USB_GSPCA_VICAM=m +CONFIG_USB_GSPCA_XIRLINK_CIT=m +CONFIG_USB_GSPCA_ZC3XX=m +CONFIG_USB_PWC=m +CONFIG_VIDEO_CPIA2=m +CONFIG_USB_ZR364XX=m +CONFIG_USB_STKWEBCAM=m +CONFIG_USB_S2255=m +CONFIG_VIDEO_USBTV=m +CONFIG_VIDEO_PVRUSB2=m +CONFIG_VIDEO_HDPVR=m +CONFIG_VIDEO_STK1160_COMMON=m +CONFIG_VIDEO_GO7007=m +CONFIG_VIDEO_GO7007_USB=m +CONFIG_VIDEO_GO7007_USB_S2250_BOARD=m +CONFIG_VIDEO_AU0828=m +CONFIG_VIDEO_AU0828_RC=y +CONFIG_VIDEO_CX231XX=m +CONFIG_VIDEO_CX231XX_ALSA=m +CONFIG_VIDEO_CX231XX_DVB=m +CONFIG_DVB_USB=m +CONFIG_DVB_USB_A800=m +CONFIG_DVB_USB_DIBUSB_MB=m +CONFIG_DVB_USB_DIBUSB_MB_FAULTY=y +CONFIG_DVB_USB_DIBUSB_MC=m +CONFIG_DVB_USB_DIB0700=m +CONFIG_DVB_USB_UMT_010=m +CONFIG_DVB_USB_CXUSB=m +CONFIG_DVB_USB_M920X=m +CONFIG_DVB_USB_DIGITV=m +CONFIG_DVB_USB_VP7045=m +CONFIG_DVB_USB_VP702X=m +CONFIG_DVB_USB_GP8PSK=m +CONFIG_DVB_USB_NOVA_T_USB2=m +CONFIG_DVB_USB_TTUSB2=m +CONFIG_DVB_USB_DTT200U=m +CONFIG_DVB_USB_OPERA1=m +CONFIG_DVB_USB_AF9005=m +CONFIG_DVB_USB_AF9005_REMOTE=m +CONFIG_DVB_USB_PCTV452E=m +CONFIG_DVB_USB_DW2102=m +CONFIG_DVB_USB_CINERGY_T2=m +CONFIG_DVB_USB_DTV5100=m +CONFIG_DVB_USB_AZ6027=m +CONFIG_DVB_USB_TECHNISAT_USB2=m +CONFIG_DVB_USB_V2=m +CONFIG_DVB_USB_AF9015=m +CONFIG_DVB_USB_AF9035=m +CONFIG_DVB_USB_ANYSEE=m +CONFIG_DVB_USB_AU6610=m +CONFIG_DVB_USB_AZ6007=m +CONFIG_DVB_USB_CE6230=m +CONFIG_DVB_USB_EC168=m +CONFIG_DVB_USB_GL861=m +CONFIG_DVB_USB_LME2510=m +CONFIG_DVB_USB_MXL111SF=m +CONFIG_DVB_USB_RTL28XXU=m +CONFIG_DVB_USB_DVBSKY=m +CONFIG_DVB_USB_ZD1301=m +CONFIG_SMS_USB_DRV=m +CONFIG_DVB_B2C2_FLEXCOP_USB=m +CONFIG_DVB_AS102=m +CONFIG_VIDEO_EM28XX=m +CONFIG_VIDEO_EM28XX_V4L2=m +CONFIG_VIDEO_EM28XX_ALSA=m +CONFIG_VIDEO_EM28XX_DVB=m +CONFIG_USB_AIRSPY=m +CONFIG_USB_HACKRF=m +CONFIG_USB_MSI2500=m +CONFIG_RADIO_SI470X=y +CONFIG_USB_SI470X=m +CONFIG_RADIO_SI4713=m +CONFIG_I2C_SI4713=m +CONFIG_USB_MR800=m +CONFIG_RADIO_SHARK=m +CONFIG_RADIO_SHARK2=m +CONFIG_USB_KEENE=m +CONFIG_USB_RAREMONO=m +CONFIG_USB_MA901=m +CONFIG_RADIO_WL128X=m +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_VIDEO_MUX=m +CONFIG_VIDEO_TI_VIP=m +CONFIG_V4L_MEM2MEM_DRIVERS=y +CONFIG_VIDEO_TI_VPE=m +CONFIG_SMS_SDIO_DRV=m +CONFIG_VIDEO_TVAUDIO=m +CONFIG_VIDEO_TDA7432=m +CONFIG_VIDEO_TDA9840=m +CONFIG_VIDEO_TEA6415C=m +CONFIG_VIDEO_TEA6420=m +CONFIG_VIDEO_CS3308=m +CONFIG_VIDEO_CS5345=m +CONFIG_VIDEO_TLV320AIC23B=m +CONFIG_VIDEO_WM8739=m +CONFIG_VIDEO_VP27SMPX=m +CONFIG_VIDEO_SAA6588=m +CONFIG_VIDEO_BT819=m +CONFIG_VIDEO_BT856=m +CONFIG_VIDEO_KS0127=m +CONFIG_VIDEO_SAA7110=m +CONFIG_VIDEO_VPX3220=m +CONFIG_VIDEO_SAA717X=m +CONFIG_VIDEO_SAA7127=m +CONFIG_VIDEO_SAA7185=m +CONFIG_VIDEO_ADV7170=m +CONFIG_VIDEO_ADV7175=m +CONFIG_VIDEO_UPD64031A=m +CONFIG_VIDEO_UPD64083=m +CONFIG_VIDEO_SAA6752HS=m +CONFIG_VIDEO_M52790=m +CONFIG_VIDEO_IMX219=m +CONFIG_VIDEO_IMX290=m +CONFIG_VIDEO_OV5640=m +CONFIG_VIDEO_OV5645=m +CONFIG_VIDEO_OV5647=m +CONFIG_VIDEO_OV7251=m +CONFIG_VIDEO_OV7670=m +CONFIG_VIDEO_OV1063X=m +CONFIG_VIDEO_MT9P031=m +CONFIG_VIDEO_SR030PC30=m +CONFIG_VIDEO_NOON010PC30=m +CONFIG_VIDEO_DS90UB953=m +CONFIG_VIDEO_DS90UB960=m +CONFIG_CXD2880_SPI_DRV=m +CONFIG_MEDIA_TUNER_MT2131=m +CONFIG_MEDIA_TUNER_M88RS6000T=m +CONFIG_MEDIA_TUNER_MXL301RF=m +CONFIG_MEDIA_TUNER_QM1D1B0004=m +CONFIG_DVB_STV0910=m +CONFIG_DVB_STV6111=m +CONFIG_DVB_MXL5XX=m +CONFIG_DVB_CX24110=m +CONFIG_DVB_ZL10036=m +CONFIG_DVB_TDA8083=m +CONFIG_DVB_TDA8261=m +CONFIG_DVB_VES1X93=m +CONFIG_DVB_TUA6100=m +CONFIG_DVB_CX24117=m +CONFIG_DVB_MB86A16=m +CONFIG_DVB_SP8870=m +CONFIG_DVB_SP887X=m +CONFIG_DVB_CX22700=m +CONFIG_DVB_S5H1432=m +CONFIG_DVB_L64781=m +CONFIG_DVB_DIB9000=m +CONFIG_DVB_STV0367=m +CONFIG_DVB_VES1820=m +CONFIG_DVB_TDA10021=m +CONFIG_DVB_OR51211=m +CONFIG_DVB_OR51132=m +CONFIG_DVB_MN88443X=m +CONFIG_DVB_LNBH25=m +CONFIG_DVB_LNBH29=m +CONFIG_DVB_ISL6405=m +CONFIG_DVB_LGS8GL5=m +CONFIG_DVB_TDA665x=m +CONFIG_DVB_HORUS3A=m +CONFIG_DVB_ASCOT2E=m +CONFIG_DVB_HELENE=m +CONFIG_DVB_CXD2099=m +CONFIG_DVB_DUMMY_FE=m +CONFIG_DRM=y +CONFIG_DRM_DP_AUX_CHARDEV=y +CONFIG_DRM_LOAD_EDID_FIRMWARE=y +CONFIG_DRM_I2C_NXP_TDA998X=y +CONFIG_DRM_VGEM=m +CONFIG_DRM_UDL=m +CONFIG_DRM_OMAP=y +CONFIG_DRM_OMAP_WB=y +CONFIG_OMAP5_DSS_HDMI=y +CONFIG_DRM_TILCDC=y +CONFIG_DRM_PANEL_SIMPLE=y +CONFIG_DRM_PANEL_LG_LB035Q02=m +CONFIG_DRM_PANEL_NEC_NL8048HL11=m +CONFIG_DRM_PANEL_ORISETECH_OTM8009A=y +CONFIG_DRM_PANEL_OSD_OSD101T2587_53TS=y +CONFIG_DRM_PANEL_SHARP_LS037V7DW01=m +CONFIG_DRM_PANEL_SONY_ACX565AKM=m +CONFIG_DRM_PANEL_TPO_TD028TTEC1=m +CONFIG_DRM_PANEL_TPO_TD043MTEA1=m +CONFIG_DRM_DISPLAY_CONNECTOR=y +CONFIG_DRM_ITE_IT66121=m +CONFIG_DRM_LVDS_CODEC=y +CONFIG_DRM_SII902X=y +CONFIG_DRM_SIMPLE_BRIDGE=m +CONFIG_DRM_TOSHIBA_TC358767=y +CONFIG_DRM_TOSHIBA_TC358768=y +CONFIG_DRM_TI_TFP410=y +CONFIG_DRM_TI_TPD12S015=y +CONFIG_DRM_I2C_ADV7511=y +CONFIG_DRM_I2C_ADV7511_AUDIO=y +CONFIG_DRM_ETNAVIV=y +CONFIG_DRM_GM12U320=m +CONFIG_TINYDRM_HX8357D=m +CONFIG_TINYDRM_ILI9225=m +CONFIG_TINYDRM_ILI9341=m +CONFIG_TINYDRM_ILI9486=m +CONFIG_TINYDRM_MI0283QT=m +CONFIG_TINYDRM_REPAPER=m +CONFIG_TINYDRM_ST7586=m +CONFIG_TINYDRM_ST7735R=m +CONFIG_DRM_TIDSS=y +CONFIG_DRM_LEGACY=y +CONFIG_FIRMWARE_EDID=y +CONFIG_FB_TILEBLITTING=y +CONFIG_FB_SMSCUFX=m +CONFIG_FB_UDL=m +CONFIG_FB_SIMPLE=y +CONFIG_FB_SSD1307=y +CONFIG_LCD_CLASS_DEVICE=y +CONFIG_LCD_PLATFORM=y +CONFIG_BACKLIGHT_PWM=y +CONFIG_BACKLIGHT_GPIO=y +CONFIG_BACKLIGHT_LED=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION=y +CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_CLUT224 is not set +CONFIG_SOUND=m +CONFIG_SND=m +CONFIG_SND_OSSEMUL=y +CONFIG_SND_MIXER_OSS=m +CONFIG_SND_PCM_OSS=m +CONFIG_SND_HRTIMER=m +CONFIG_SND_DYNAMIC_MINORS=y +CONFIG_SND_SEQUENCER=m +CONFIG_SND_SEQ_DUMMY=m +CONFIG_SND_DUMMY=m +CONFIG_SND_ALOOP=m +CONFIG_SND_VIRMIDI=m +CONFIG_SND_MTPAV=m +CONFIG_SND_SERIAL_U16550=m +CONFIG_SND_MPU401=m +CONFIG_SND_HDA_PREALLOC_SIZE=2048 +CONFIG_SND_USB_AUDIO=m +CONFIG_SND_USB_UA101=m +CONFIG_SND_USB_CAIAQ=m +CONFIG_SND_USB_CAIAQ_INPUT=y +CONFIG_SND_USB_6FIRE=m +CONFIG_SND_USB_HIFACE=m +CONFIG_SND_BCD2000=m +CONFIG_SND_USB_POD=m +CONFIG_SND_USB_PODHD=m +CONFIG_SND_USB_TONEPORT=m +CONFIG_SND_USB_VARIAX=m +CONFIG_SND_SOC=m +CONFIG_SND_SOC_DAVINCI_MCASP=m +CONFIG_SND_SOC_OMAP_DMIC=m +CONFIG_SND_SOC_OMAP_MCBSP=m +CONFIG_SND_SOC_OMAP_MCPDM=m +CONFIG_SND_SOC_OMAP_HDMI=m +CONFIG_SND_SOC_ADAU1701=m +CONFIG_SND_SOC_ADAU7002=m +CONFIG_SND_SOC_AK4554=m +CONFIG_SND_SOC_CS42L51_I2C=m +CONFIG_SND_SOC_CS4265=m +CONFIG_SND_SOC_CS4271_I2C=m +CONFIG_SND_SOC_DMIC=m +CONFIG_SND_SOC_ES8328_I2C=m +CONFIG_SND_SOC_ES8328_SPI=m +CONFIG_SND_SOC_MAX98357A=m +CONFIG_SND_SOC_PCM3168A_I2C=m +CONFIG_SND_SOC_PCM5102A=m +CONFIG_SND_SOC_PCM512x_I2C=m +CONFIG_SND_SOC_SGTL5000=m +CONFIG_SND_SOC_SIMPLE_AMPLIFIER=m +CONFIG_SND_SOC_SPDIF=m +CONFIG_SND_SOC_TLV320AIC23_I2C=m +CONFIG_SND_SOC_TLV320AIC31XX=m +CONFIG_SND_SOC_TLV320AIC3X=m +CONFIG_SND_SOC_TS3A227E=m +CONFIG_SND_SOC_WM8753=m +CONFIG_SND_SOC_WM8804_I2C=m +CONFIG_SND_SOC_WM8903=m +CONFIG_SND_SOC_WM8904=m +CONFIG_SND_SOC_WM8960=m +CONFIG_SND_SOC_TPA6130A2=m +CONFIG_SND_SIMPLE_CARD=m +CONFIG_SND_AUDIO_GRAPH_CARD=m +CONFIG_HID_BATTERY_STRENGTH=y +CONFIG_HIDRAW=y +CONFIG_UHID=y +CONFIG_HID_A4TECH=m +CONFIG_HID_ACCUTOUCH=m +CONFIG_HID_ACRUX=m +CONFIG_HID_ACRUX_FF=y +CONFIG_HID_APPLE=m +CONFIG_HID_ASUS=m +CONFIG_HID_AUREAL=m +CONFIG_HID_BELKIN=m +CONFIG_HID_BETOP_FF=m +CONFIG_HID_BIGBEN_FF=m +CONFIG_HID_CHERRY=m +CONFIG_HID_CHICONY=m +CONFIG_HID_CORSAIR=m +CONFIG_HID_COUGAR=m +CONFIG_HID_MACALLY=m +CONFIG_HID_PRODIKEYS=m +CONFIG_HID_CMEDIA=m +CONFIG_HID_CP2112=m +CONFIG_HID_CREATIVE_SB0540=m +CONFIG_HID_CYPRESS=m +CONFIG_HID_DRAGONRISE=m +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_EMS_FF=m +CONFIG_HID_ELAN=m +CONFIG_HID_ELECOM=m +CONFIG_HID_ELO=m +CONFIG_HID_EZKEY=m +CONFIG_HID_GEMBIRD=m +CONFIG_HID_GFRM=m +CONFIG_HID_GLORIOUS=m +CONFIG_HID_HOLTEK=m +CONFIG_HOLTEK_FF=y +CONFIG_HID_VIVALDI=m +CONFIG_HID_GT683R=m +CONFIG_HID_KEYTOUCH=m +CONFIG_HID_KYE=m +CONFIG_HID_UCLOGIC=m +CONFIG_HID_WALTOP=m +CONFIG_HID_VIEWSONIC=m +CONFIG_HID_GYRATION=m +CONFIG_HID_ICADE=m +CONFIG_HID_ITE=m +CONFIG_HID_JABRA=m +CONFIG_HID_TWINHAN=m +CONFIG_HID_KENSINGTON=m +CONFIG_HID_LCPOWER=m +CONFIG_HID_LENOVO=m +CONFIG_HID_LOGITECH=y +CONFIG_HID_LOGITECH_DJ=y +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGIG940_FF=y +CONFIG_HID_MAGICMOUSE=m +CONFIG_HID_MALTRON=m +CONFIG_HID_MAYFLASH=m +CONFIG_HID_REDRAGON=m +CONFIG_HID_MICROSOFT=m +CONFIG_HID_MONTEREY=m +CONFIG_HID_MULTITOUCH=m +CONFIG_HID_NTI=m +CONFIG_HID_NTRIG=m +CONFIG_HID_ORTEK=m +CONFIG_HID_PANTHERLORD=m +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PENMOUNT=m +CONFIG_HID_PETALYNX=m +CONFIG_HID_PICOLCD=m +CONFIG_HID_PICOLCD_FB=y +CONFIG_HID_PICOLCD_BACKLIGHT=y +CONFIG_HID_PICOLCD_LEDS=y +CONFIG_HID_PICOLCD_CIR=y +CONFIG_HID_PLANTRONICS=m +CONFIG_HID_PRIMAX=m +CONFIG_HID_RETRODE=m +CONFIG_HID_ROCCAT=m +CONFIG_HID_SAITEK=m +CONFIG_HID_SAMSUNG=m +CONFIG_HID_SONY=m +CONFIG_SONY_FF=y +CONFIG_HID_SPEEDLINK=m +CONFIG_HID_STEAM=m +CONFIG_HID_STEELSERIES=m +CONFIG_HID_SUNPLUS=m +CONFIG_HID_RMI=m +CONFIG_HID_GREENASIA=m +CONFIG_GREENASIA_FF=y +CONFIG_HID_SMARTJOYPLUS=m +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_HID_TIVO=m +CONFIG_HID_TOPSEED=m +CONFIG_HID_THINGM=m +CONFIG_HID_THRUSTMASTER=m +CONFIG_THRUSTMASTER_FF=y +CONFIG_HID_UDRAW_PS3=m +CONFIG_HID_U2FZERO=m +CONFIG_HID_WACOM=m +CONFIG_HID_WIIMOTE=m +CONFIG_HID_XINMO=m +CONFIG_HID_ZEROPLUS=m +CONFIG_ZEROPLUS_FF=y +CONFIG_HID_ZYDACRON=m +CONFIG_HID_SENSOR_HUB=m +CONFIG_HID_SENSOR_CUSTOM_SENSOR=m +CONFIG_HID_ALPS=m +CONFIG_HID_MCP2221=m +CONFIG_HID_PID=y +CONFIG_USB_HIDDEV=y +CONFIG_I2C_HID=y +CONFIG_USB_LED_TRIG=y +CONFIG_USB_ULPI_BUS=m +CONFIG_USB=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_DYNAMIC_MINORS=y +CONFIG_USB_OTG=y +CONFIG_USB_LEDS_TRIGGER_USBPORT=m +CONFIG_USB_MON=m +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +CONFIG_USB_PRINTER=m +CONFIG_USB_TMC=m +CONFIG_USB_STORAGE=m +CONFIG_USB_STORAGE_REALTEK=m +CONFIG_USB_STORAGE_DATAFAB=m +CONFIG_USB_STORAGE_FREECOM=m +CONFIG_USB_STORAGE_ISD200=m +CONFIG_USB_STORAGE_USBAT=m +CONFIG_USB_STORAGE_SDDR09=m +CONFIG_USB_STORAGE_SDDR55=m +CONFIG_USB_STORAGE_JUMPSHOT=m +CONFIG_USB_STORAGE_ALAUDA=m +CONFIG_USB_STORAGE_ONETOUCH=m +CONFIG_USB_STORAGE_KARMA=m +CONFIG_USB_STORAGE_CYPRESS_ATACB=m +CONFIG_USB_STORAGE_ENE_UB6250=m +CONFIG_USB_UAS=m +CONFIG_USB_MDC800=m +CONFIG_USB_MICROTEK=m +CONFIG_USBIP_CORE=m +CONFIG_USBIP_VHCI_HCD=m +CONFIG_USBIP_VHCI_HC_PORTS=15 +CONFIG_USBIP_VHCI_NR_HCS=8 +CONFIG_USBIP_HOST=m +CONFIG_USBIP_VUDC=m +CONFIG_USB_MUSB_HDRC=y +CONFIG_USB_MUSB_DSPS=y +CONFIG_MUSB_PIO_ONLY=y +CONFIG_USB_DWC3=y +CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL_GENERIC=y +CONFIG_USB_SERIAL_SIMPLE=m +CONFIG_USB_SERIAL_AIRCABLE=m +CONFIG_USB_SERIAL_ARK3116=m +CONFIG_USB_SERIAL_BELKIN=m +CONFIG_USB_SERIAL_CH341=m +CONFIG_USB_SERIAL_WHITEHEAT=m +CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m +CONFIG_USB_SERIAL_CP210X=m +CONFIG_USB_SERIAL_CYPRESS_M8=m +CONFIG_USB_SERIAL_EMPEG=m +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_VISOR=m +CONFIG_USB_SERIAL_IPAQ=m +CONFIG_USB_SERIAL_IR=m +CONFIG_USB_SERIAL_EDGEPORT=m +CONFIG_USB_SERIAL_EDGEPORT_TI=m +CONFIG_USB_SERIAL_F81232=m +CONFIG_USB_SERIAL_F8153X=m +CONFIG_USB_SERIAL_GARMIN=m +CONFIG_USB_SERIAL_IPW=m +CONFIG_USB_SERIAL_IUU=m +CONFIG_USB_SERIAL_KEYSPAN_PDA=m +CONFIG_USB_SERIAL_KEYSPAN=m +CONFIG_USB_SERIAL_KLSI=m +CONFIG_USB_SERIAL_KOBIL_SCT=m +CONFIG_USB_SERIAL_MCT_U232=m +CONFIG_USB_SERIAL_METRO=m +CONFIG_USB_SERIAL_MOS7720=m +CONFIG_USB_SERIAL_MOS7840=m +CONFIG_USB_SERIAL_MXUPORT=m +CONFIG_USB_SERIAL_NAVMAN=m +CONFIG_USB_SERIAL_PL2303=m +CONFIG_USB_SERIAL_OTI6858=m +CONFIG_USB_SERIAL_QCAUX=m +CONFIG_USB_SERIAL_QUALCOMM=m +CONFIG_USB_SERIAL_SPCP8X5=m +CONFIG_USB_SERIAL_SAFE=m +CONFIG_USB_SERIAL_SIERRAWIRELESS=m +CONFIG_USB_SERIAL_SYMBOL=m +CONFIG_USB_SERIAL_TI=m +CONFIG_USB_SERIAL_CYBERJACK=m +CONFIG_USB_SERIAL_XIRCOM=m +CONFIG_USB_SERIAL_OPTION=m +CONFIG_USB_SERIAL_OMNINET=m +CONFIG_USB_SERIAL_OPTICON=m +CONFIG_USB_SERIAL_XSENS_MT=m +CONFIG_USB_SERIAL_WISHBONE=m +CONFIG_USB_SERIAL_SSU100=m +CONFIG_USB_SERIAL_QT2=m +CONFIG_USB_SERIAL_UPD78F0730=m +CONFIG_USB_SERIAL_DEBUG=m +CONFIG_USB_EMI62=m +CONFIG_USB_EMI26=m +CONFIG_USB_ADUTUX=m +CONFIG_USB_SEVSEG=m +CONFIG_USB_LEGOTOWER=m +CONFIG_USB_LCD=m +CONFIG_USB_CYPRESS_CY7C63=m +CONFIG_USB_CYTHERM=m +CONFIG_USB_IDMOUSE=m +CONFIG_USB_FTDI_ELAN=m +CONFIG_USB_APPLEDISPLAY=m +CONFIG_APPLE_MFI_FASTCHARGE=m +CONFIG_USB_SISUSBVGA=m +CONFIG_USB_LD=m +CONFIG_USB_TRANCEVIBRATOR=m +CONFIG_USB_IOWARRIOR=m +CONFIG_USB_TEST=m +CONFIG_USB_EHSET_TEST_FIXTURE=m +CONFIG_USB_ISIGHTFW=m +CONFIG_USB_YUREX=m +CONFIG_USB_HSIC_USB3503=m +CONFIG_USB_CHAOSKEY=m +CONFIG_NOP_USB_XCEIV=y +CONFIG_AM335X_PHY_USB=y +CONFIG_USB_GPIO_VBUS=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_DUMMY_HCD=m +CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_SERIAL=y +CONFIG_USB_CONFIGFS_ACM=y +CONFIG_USB_CONFIGFS_OBEX=y +CONFIG_USB_CONFIGFS_NCM=y +CONFIG_USB_CONFIGFS_ECM=y +CONFIG_USB_CONFIGFS_ECM_SUBSET=y +CONFIG_USB_CONFIGFS_RNDIS=y +CONFIG_USB_CONFIGFS_EEM=y +CONFIG_USB_CONFIGFS_PHONET=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +CONFIG_USB_CONFIGFS_F_LB_SS=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_UAC1=y +CONFIG_USB_CONFIGFS_F_UAC2=y +CONFIG_USB_CONFIGFS_F_MIDI=y +CONFIG_USB_CONFIGFS_F_HID=y +CONFIG_USB_CONFIGFS_F_UVC=y +CONFIG_USB_CONFIGFS_F_PRINTER=y +CONFIG_USB_ZERO=m +CONFIG_USB_AUDIO=m +CONFIG_USB_ETH=m +CONFIG_USB_G_NCM=m +CONFIG_USB_GADGETFS=m +CONFIG_USB_FUNCTIONFS=m +CONFIG_USB_FUNCTIONFS_ETH=y +CONFIG_USB_FUNCTIONFS_RNDIS=y +CONFIG_USB_FUNCTIONFS_GENERIC=y +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_G_SERIAL=m +CONFIG_USB_MIDI_GADGET=m +CONFIG_USB_G_PRINTER=m +CONFIG_USB_CDC_COMPOSITE=m +CONFIG_USB_G_NOKIA=m +CONFIG_USB_G_ACM_MS=m +CONFIG_USB_G_MULTI=m +CONFIG_USB_G_HID=m +CONFIG_USB_G_DBGP=m +CONFIG_USB_G_WEBCAM=m +CONFIG_TYPEC=y +CONFIG_TYPEC_HD3SS3220=y +CONFIG_MMC=y +CONFIG_PWRSEQ_SD8787=m +CONFIG_MMC_BLOCK_MINORS=256 +CONFIG_SDIO_UART=m +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SPI=m +CONFIG_MMC_VUB300=m +CONFIG_MMC_USHC=m +CONFIG_MMC_SDHCI_OMAP=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_BRIGHTNESS_HW_CHANGED=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_LP3944=m +CONFIG_LEDS_LP55XX_COMMON=m +CONFIG_LEDS_LP5523=m +CONFIG_LEDS_PCA955X=m +CONFIG_LEDS_DAC124S085=m +CONFIG_LEDS_PWM=m +CONFIG_LEDS_REGULATOR=m +CONFIG_LEDS_BD2802=m +CONFIG_LEDS_LT3593=m +CONFIG_LEDS_TCA6507=m +CONFIG_LEDS_SYSCON=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_ONESHOT=y +CONFIG_LEDS_TRIGGER_DISK=y +CONFIG_LEDS_TRIGGER_MTD=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_BACKLIGHT=y +CONFIG_LEDS_TRIGGER_CPU=y +CONFIG_LEDS_TRIGGER_ACTIVITY=y +CONFIG_LEDS_TRIGGER_GPIO=y +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y +CONFIG_LEDS_TRIGGER_TRANSIENT=m +CONFIG_LEDS_TRIGGER_CAMERA=m +CONFIG_LEDS_TRIGGER_PANIC=y +CONFIG_LEDS_TRIGGER_NETDEV=y +CONFIG_LEDS_TRIGGER_PATTERN=m +CONFIG_LEDS_TRIGGER_AUDIO=m +CONFIG_ACCESSIBILITY=y +CONFIG_A11Y_BRAILLE_CONSOLE=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_ABB5ZES3=y +CONFIG_RTC_DRV_ABEOZ9=y +CONFIG_RTC_DRV_ABX80X=y +CONFIG_RTC_DRV_DS1307=y +CONFIG_RTC_DRV_DS1374=y +CONFIG_RTC_DRV_DS1374_WDT=y +CONFIG_RTC_DRV_DS1672=y +CONFIG_RTC_DRV_HYM8563=y +CONFIG_RTC_DRV_MAX6900=y +CONFIG_RTC_DRV_RS5C372=y +CONFIG_RTC_DRV_ISL1208=y +CONFIG_RTC_DRV_ISL12022=y +CONFIG_RTC_DRV_ISL12026=y +CONFIG_RTC_DRV_X1205=y +CONFIG_RTC_DRV_PCF8523=y +CONFIG_RTC_DRV_PCF85063=y +CONFIG_RTC_DRV_PCF85363=y +CONFIG_RTC_DRV_PCF8563=y +CONFIG_RTC_DRV_PCF8583=y +CONFIG_RTC_DRV_M41T80=y +CONFIG_RTC_DRV_M41T80_WDT=y +CONFIG_RTC_DRV_BQ32K=y +CONFIG_RTC_DRV_PALMAS=y +CONFIG_RTC_DRV_S35390A=y +CONFIG_RTC_DRV_FM3130=y +CONFIG_RTC_DRV_RX8010=y +CONFIG_RTC_DRV_RX8581=y +CONFIG_RTC_DRV_RX8025=y +CONFIG_RTC_DRV_EM3027=y +CONFIG_RTC_DRV_RV8803=y +CONFIG_RTC_DRV_M41T93=y +CONFIG_RTC_DRV_M41T94=y +CONFIG_RTC_DRV_DS1302=y +CONFIG_RTC_DRV_DS1305=y +CONFIG_RTC_DRV_DS1343=y +CONFIG_RTC_DRV_DS1347=y +CONFIG_RTC_DRV_DS1390=y +CONFIG_RTC_DRV_MAX6916=y +CONFIG_RTC_DRV_R9701=y +CONFIG_RTC_DRV_RX4581=y +CONFIG_RTC_DRV_RX6110=y +CONFIG_RTC_DRV_RS5C348=y +CONFIG_RTC_DRV_MAX6902=y +CONFIG_RTC_DRV_PCF2123=y +CONFIG_RTC_DRV_MCP795=y +CONFIG_RTC_DRV_DS3232=y +CONFIG_RTC_DRV_PCF2127=y +CONFIG_RTC_DRV_RV3029C2=y +CONFIG_RTC_DRV_DS1286=m +CONFIG_RTC_DRV_DS1511=m +CONFIG_RTC_DRV_DS1553=m +CONFIG_RTC_DRV_DS1685_FAMILY=m +CONFIG_RTC_DRV_DS1742=m +CONFIG_RTC_DRV_DS2404=m +CONFIG_RTC_DRV_EFI=y +CONFIG_RTC_DRV_STK17TA8=m +CONFIG_RTC_DRV_M48T86=m +CONFIG_RTC_DRV_M48T35=m +CONFIG_RTC_DRV_M48T59=m +CONFIG_RTC_DRV_MSM6242=m +CONFIG_RTC_DRV_BQ4802=m +CONFIG_RTC_DRV_RP5C01=m +CONFIG_RTC_DRV_V3020=m +CONFIG_RTC_DRV_OMAP=y +CONFIG_RTC_DRV_HID_SENSOR_TIME=m +CONFIG_DMADEVICES=y +CONFIG_TI_CPPI41=y +CONFIG_ASYNC_TX_DMA=y +CONFIG_AUXDISPLAY=y +CONFIG_HD44780=m +CONFIG_SEEED_I2C_HD44780=m +CONFIG_IMG_ASCII_LCD=m +CONFIG_HT16K33=m +CONFIG_UIO=m +CONFIG_UIO_PDRV_GENIRQ=m +CONFIG_UIO_PRUSS=m +CONFIG_VIRT_DRIVERS=y +CONFIG_VIRTIO_BALLOON=m +CONFIG_VIRTIO_INPUT=m +CONFIG_VIRTIO_MMIO=m +CONFIG_GREYBUS=m +CONFIG_GREYBUS_ES2=m +CONFIG_STAGING=y +CONFIG_RTLLIB=m +CONFIG_RTL8723BS=m +CONFIG_R8712U=m +CONFIG_R8188EU=m +CONFIG_ADIS16203=m +CONFIG_ADIS16240=m +CONFIG_AD7816=m +CONFIG_AD7280=m +CONFIG_ADT7316=m +CONFIG_ADT7316_I2C=m +CONFIG_AD7150=m +CONFIG_AD7746=m +CONFIG_AD9832=m +CONFIG_AD9834=m +CONFIG_AD5933=m +CONFIG_ADE7854=m +CONFIG_AD2S1210=m +CONFIG_FB_TFT=m +CONFIG_FB_TFT_AGM1264K_FL=m +CONFIG_FB_TFT_BD663474=m +CONFIG_FB_TFT_HX8340BN=m +CONFIG_FB_TFT_HX8347D=m +CONFIG_FB_TFT_HX8353D=m +CONFIG_FB_TFT_HX8357D=m +CONFIG_FB_TFT_ILI9163=m +CONFIG_FB_TFT_ILI9320=m +CONFIG_FB_TFT_ILI9325=m +CONFIG_FB_TFT_ILI9340=m +CONFIG_FB_TFT_ILI9341=m +CONFIG_FB_TFT_ILI9481=m +CONFIG_FB_TFT_ILI9486=m +CONFIG_FB_TFT_PCD8544=m +CONFIG_FB_TFT_RA8875=m +CONFIG_FB_TFT_S6D02A1=m +CONFIG_FB_TFT_S6D1121=m +CONFIG_FB_TFT_SH1106=m +CONFIG_FB_TFT_SSD1289=m +CONFIG_FB_TFT_SSD1305=m +CONFIG_FB_TFT_SSD1306=m +CONFIG_FB_TFT_SSD1331=m +CONFIG_FB_TFT_SSD1351=m +CONFIG_FB_TFT_ST7735R=m +CONFIG_FB_TFT_ST7789V=m +CONFIG_FB_TFT_TINYLCD=m +CONFIG_FB_TFT_TLS8204=m +CONFIG_FB_TFT_UC1611=m +CONFIG_FB_TFT_UC1701=m +CONFIG_FB_TFT_UPD161704=m +CONFIG_FB_TFT_WATTEROTT=m +CONFIG_GREYBUS_AUDIO=m +CONFIG_GREYBUS_BOOTROM=m +CONFIG_GREYBUS_FIRMWARE=m +CONFIG_GREYBUS_HID=m +CONFIG_GREYBUS_LIGHT=m +CONFIG_GREYBUS_LOG=m +CONFIG_GREYBUS_LOOPBACK=m +CONFIG_GREYBUS_POWER=m +CONFIG_GREYBUS_RAW=m +CONFIG_GREYBUS_VIBRATOR=m +CONFIG_GREYBUS_BRIDGED_PHY=m +CONFIG_GREYBUS_GPIO=m +CONFIG_GREYBUS_I2C=m +CONFIG_GREYBUS_PWM=m +CONFIG_GREYBUS_SDIO=m +CONFIG_GREYBUS_SPI=m +CONFIG_GREYBUS_UART=m +CONFIG_GREYBUS_USB=m +CONFIG_COMMON_CLK_PALMAS=y +CONFIG_COMMON_CLK_TI_ADPLL=y +CONFIG_HWSPINLOCK=y +CONFIG_HWSPINLOCK_OMAP=y +CONFIG_OMAP2PLUS_MBOX=y +CONFIG_OMAP_IOMMU=y +CONFIG_REMOTEPROC=y +CONFIG_REMOTEPROC_CDEV=y +CONFIG_OMAP_REMOTEPROC=m +CONFIG_WKUP_M3_RPROC=y +CONFIG_RPMSG_VIRTIO=m +CONFIG_RPMSG_PRU=m +CONFIG_SOC_TI=y +CONFIG_AMX3_PM=m +CONFIG_WKUP_M3_IPC=m +CONFIG_TI_PRUSS=m +CONFIG_PM_DEVFREQ=y +CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y +CONFIG_DEVFREQ_GOV_PERFORMANCE=y +CONFIG_DEVFREQ_GOV_POWERSAVE=y +CONFIG_DEVFREQ_GOV_USERSPACE=y +CONFIG_DEVFREQ_GOV_PASSIVE=y +CONFIG_PM_DEVFREQ_EVENT=y +CONFIG_EXTCON_GPIO=y +CONFIG_EXTCON_PALMAS=y +CONFIG_EXTCON_USB_GPIO=y +CONFIG_TI_EMIF=y +CONFIG_TI_EMIF_SRAM=y +CONFIG_IIO=y +CONFIG_IIO_SW_DEVICE=m +CONFIG_IIO_SW_TRIGGER=m +CONFIG_ADIS16201=m +CONFIG_ADIS16209=m +CONFIG_ADXL345_I2C=m +CONFIG_ADXL345_SPI=m +CONFIG_ADXL372_SPI=m +CONFIG_ADXL372_I2C=m +CONFIG_BMA180=m +CONFIG_BMA220=m +CONFIG_BMA400=m +CONFIG_BMC150_ACCEL=m +CONFIG_DA280=m +CONFIG_DA311=m +CONFIG_DMARD06=m +CONFIG_DMARD09=m +CONFIG_DMARD10=m +CONFIG_HID_SENSOR_ACCEL_3D=m +CONFIG_IIO_ST_ACCEL_3AXIS=m +CONFIG_KXSD9=m +CONFIG_KXCJK1013=m +CONFIG_MC3230=m +CONFIG_MMA7455_I2C=m +CONFIG_MMA7455_SPI=m +CONFIG_MMA7660=m +CONFIG_MMA8452=m +CONFIG_MMA9551=m +CONFIG_MMA9553=m +CONFIG_MXC4005=m +CONFIG_MXC6255=m +CONFIG_SCA3000=m +CONFIG_STK8312=m +CONFIG_STK8BA50=m +CONFIG_AD7091R5=m +CONFIG_AD7124=m +CONFIG_AD7192=m +CONFIG_AD7266=m +CONFIG_AD7291=m +CONFIG_AD7292=m +CONFIG_AD7298=m +CONFIG_AD7476=m +CONFIG_AD7606_IFACE_PARALLEL=m +CONFIG_AD7606_IFACE_SPI=m +CONFIG_AD7766=m +CONFIG_AD7768_1=m +CONFIG_AD7780=m +CONFIG_AD7791=m +CONFIG_AD7793=m +CONFIG_AD7887=m +CONFIG_AD7923=m +CONFIG_AD7949=m +CONFIG_AD799X=m +CONFIG_AD9467=m +CONFIG_ADI_AXI_ADC=m +CONFIG_CC10001_ADC=m +CONFIG_ENVELOPE_DETECTOR=m +CONFIG_HI8435=m +CONFIG_HX711=m +CONFIG_INA2XX_ADC=m +CONFIG_LTC2471=m +CONFIG_LTC2485=m +CONFIG_LTC2496=m +CONFIG_LTC2497=m +CONFIG_MAX1027=m +CONFIG_MAX11100=m +CONFIG_MAX1118=m +CONFIG_MAX1241=m +CONFIG_MAX1363=m +CONFIG_MAX9611=m +CONFIG_MCP320X=m +CONFIG_MCP3422=m +CONFIG_MCP3911=m +CONFIG_NAU7802=m +CONFIG_PALMAS_GPADC=m +CONFIG_SD_ADC_MODULATOR=m +CONFIG_STMPE_ADC=m +CONFIG_TI_ADC081C=m +CONFIG_TI_ADC0832=m +CONFIG_TI_ADC084S021=m +CONFIG_TI_ADC12138=m +CONFIG_TI_ADC108S102=m +CONFIG_TI_ADC128S052=m +CONFIG_TI_ADC161S626=m +CONFIG_TI_ADS1015=m +CONFIG_TI_ADS7950=m +CONFIG_TI_ADS8344=m +CONFIG_TI_ADS8688=m +CONFIG_TI_ADS124S08=m +CONFIG_TI_AM335X_ADC=y +CONFIG_TI_TLC4541=m +CONFIG_IIO_RESCALE=m +CONFIG_AD8366=m +CONFIG_HMC425=m +CONFIG_ATLAS_PH_SENSOR=m +CONFIG_ATLAS_EZO_SENSOR=m +CONFIG_BME680=m +CONFIG_CCS811=m +CONFIG_IAQCORE=m +CONFIG_PMS7003=m +CONFIG_SCD30_CORE=m +CONFIG_SCD30_I2C=m +CONFIG_SCD30_SERIAL=m +CONFIG_SENSIRION_SGP30=m +CONFIG_SPS30=m +CONFIG_VZ89X=m +CONFIG_AD5064=m +CONFIG_AD5360=m +CONFIG_AD5380=m +CONFIG_AD5421=m +CONFIG_AD5446=m +CONFIG_AD5449=m +CONFIG_AD5592R=m +CONFIG_AD5593R=m +CONFIG_AD5504=m +CONFIG_AD5624R_SPI=m +CONFIG_AD5686_SPI=m +CONFIG_AD5696_I2C=m +CONFIG_AD5755=m +CONFIG_AD5758=m +CONFIG_AD5761=m +CONFIG_AD5764=m +CONFIG_AD5770R=m +CONFIG_AD5791=m +CONFIG_AD7303=m +CONFIG_AD8801=m +CONFIG_DPOT_DAC=m +CONFIG_DS4424=m +CONFIG_LTC1660=m +CONFIG_LTC2632=m +CONFIG_M62332=m +CONFIG_MAX517=m +CONFIG_MAX5821=m +CONFIG_MCP4725=m +CONFIG_MCP4922=m +CONFIG_TI_DAC082S085=m +CONFIG_TI_DAC5571=m +CONFIG_TI_DAC7311=m +CONFIG_TI_DAC7612=m +CONFIG_AD9523=m +CONFIG_ADF4350=m +CONFIG_ADF4371=m +CONFIG_ADIS16080=m +CONFIG_ADIS16130=m +CONFIG_ADIS16136=m +CONFIG_ADIS16260=m +CONFIG_ADXRS290=m +CONFIG_ADXRS450=m +CONFIG_BMG160=m +CONFIG_FXAS21002C=m +CONFIG_HID_SENSOR_GYRO_3D=m +CONFIG_MPU3050_I2C=m +CONFIG_IIO_ST_GYRO_3AXIS=m +CONFIG_ITG3200=m +CONFIG_AFE4403=m +CONFIG_AFE4404=m +CONFIG_MAX30100=m +CONFIG_MAX30102=m +CONFIG_AM2315=m +CONFIG_DHT11=m +CONFIG_HDC100X=m +CONFIG_HDC2010=m +CONFIG_HID_SENSOR_HUMIDITY=m +CONFIG_HTS221=m +CONFIG_HTU21=m +CONFIG_SI7005=m +CONFIG_SI7020=m +CONFIG_ADIS16400=m +CONFIG_ADIS16460=m +CONFIG_ADIS16475=m +CONFIG_ADIS16480=m +CONFIG_BMI160_I2C=m +CONFIG_BMI160_SPI=m +CONFIG_FXOS8700_I2C=m +CONFIG_FXOS8700_SPI=m +CONFIG_KMX61=m +CONFIG_INV_ICM42600_I2C=m +CONFIG_INV_ICM42600_SPI=m +CONFIG_INV_MPU6050_I2C=m +CONFIG_INV_MPU6050_SPI=m +CONFIG_IIO_ST_LSM6DSX=m +CONFIG_ADJD_S311=m +CONFIG_ADUX1020=m +CONFIG_AL3010=m +CONFIG_AL3320A=m +CONFIG_APDS9300=m +CONFIG_APDS9960=m +CONFIG_AS73211=m +CONFIG_BH1750=m +CONFIG_BH1780=m +CONFIG_CM32181=m +CONFIG_CM3232=m +CONFIG_CM3323=m +CONFIG_CM3605=m +CONFIG_CM36651=m +CONFIG_GP2AP002=m +CONFIG_GP2AP020A00F=m +CONFIG_SENSORS_ISL29018=m +CONFIG_SENSORS_ISL29028=m +CONFIG_ISL29125=m +CONFIG_HID_SENSOR_ALS=m +CONFIG_HID_SENSOR_PROX=m +CONFIG_JSA1212=m +CONFIG_RPR0521=m +CONFIG_LTR501=m +CONFIG_LV0104CS=m +CONFIG_MAX44000=m +CONFIG_MAX44009=m +CONFIG_NOA1305=m +CONFIG_OPT3001=m +CONFIG_PA12203001=m +CONFIG_SI1133=m +CONFIG_SI1145=m +CONFIG_STK3310=m +CONFIG_ST_UVIS25=m +CONFIG_TCS3414=m +CONFIG_TCS3472=m +CONFIG_SENSORS_TSL2563=m +CONFIG_TSL2583=m +CONFIG_TSL2772=m +CONFIG_TSL4531=m +CONFIG_US5182D=m +CONFIG_VCNL4000=m +CONFIG_VCNL4035=m +CONFIG_VEML6030=m +CONFIG_VEML6070=m +CONFIG_VL6180=m +CONFIG_ZOPT2201=m +CONFIG_AK8974=m +CONFIG_AK09911=m +CONFIG_BMC150_MAGN_I2C=m +CONFIG_BMC150_MAGN_SPI=m +CONFIG_MAG3110=m +CONFIG_HID_SENSOR_MAGNETOMETER_3D=m +CONFIG_MMC35240=m +CONFIG_IIO_ST_MAGN_3AXIS=m +CONFIG_SENSORS_HMC5843_I2C=m +CONFIG_SENSORS_HMC5843_SPI=m +CONFIG_SENSORS_RM3100_I2C=m +CONFIG_SENSORS_RM3100_SPI=m +CONFIG_HID_SENSOR_INCLINOMETER_3D=m +CONFIG_HID_SENSOR_DEVICE_ROTATION=m +CONFIG_IIO_HRTIMER_TRIGGER=m +CONFIG_IIO_INTERRUPT_TRIGGER=m +CONFIG_IIO_TIGHTLOOP_TRIGGER=m +CONFIG_IIO_SYSFS_TRIGGER=m +CONFIG_AD5272=m +CONFIG_DS1803=m +CONFIG_MAX5432=m +CONFIG_MAX5481=m +CONFIG_MAX5487=m +CONFIG_MCP4018=m +CONFIG_MCP4131=m +CONFIG_MCP4531=m +CONFIG_MCP41010=m +CONFIG_TPL0102=m +CONFIG_LMP91000=m +CONFIG_ABP060MG=m +CONFIG_BMP280=m +CONFIG_DLHL60D=m +CONFIG_DPS310=m +CONFIG_HID_SENSOR_PRESS=m +CONFIG_HP03=m +CONFIG_ICP10100=m +CONFIG_MPL115_I2C=m +CONFIG_MPL115_SPI=m +CONFIG_MPL3115=m +CONFIG_MS5611=m +CONFIG_MS5611_I2C=m +CONFIG_MS5611_SPI=m +CONFIG_MS5637=m +CONFIG_IIO_ST_PRESS=m +CONFIG_T5403=m +CONFIG_HP206C=m +CONFIG_ZPA2326=m +CONFIG_AS3935=m +CONFIG_ISL29501=m +CONFIG_LIDAR_LITE_V2=m +CONFIG_MB1232=m +CONFIG_PING=m +CONFIG_RFD77402=m +CONFIG_SRF04=m +CONFIG_SX9310=m +CONFIG_SX9500=m +CONFIG_SRF08=m +CONFIG_VCNL3020=m +CONFIG_VL53L0X_I2C=m +CONFIG_AD2S90=m +CONFIG_AD2S1200=m +CONFIG_LTC2983=m +CONFIG_MAXIM_THERMOCOUPLE=m +CONFIG_HID_SENSOR_TEMP=m +CONFIG_MLX90614=m +CONFIG_MLX90632=m +CONFIG_TMP006=m +CONFIG_TMP007=m +CONFIG_TSYS01=m +CONFIG_TSYS02D=m +CONFIG_MAX31856=m +CONFIG_PWM_OMAP_DMTIMER=y +CONFIG_PWM_PCA9685=y +CONFIG_PWM_STMPE=y +CONFIG_PWM_TIECAP=y +CONFIG_PWM_TIEHRPWM=y +CONFIG_RESET_TI_SYSCON=y +CONFIG_PHY_CAN_TRANSCEIVER=m +CONFIG_OMAP_USB2=y +CONFIG_TI_PIPE3=y +CONFIG_RAS=y +CONFIG_ANDROID=y +CONFIG_FPGA=m +CONFIG_ALTERA_PR_IP_CORE=m +CONFIG_ALTERA_PR_IP_CORE_PLAT=m +CONFIG_FPGA_MGR_ALTERA_PS_SPI=m +CONFIG_FPGA_MGR_XILINX_SPI=m +CONFIG_FPGA_MGR_ICE40_SPI=m +CONFIG_FPGA_MGR_MACHXO2_SPI=m +CONFIG_FPGA_BRIDGE=m +CONFIG_ALTERA_FREEZE_BRIDGE=m +CONFIG_XILINX_PR_DECOUPLER=m +CONFIG_FPGA_REGION=m +CONFIG_OF_FPGA_REGION=m +CONFIG_MUX_ADG792A=m +CONFIG_MUX_ADGS1408=m +CONFIG_MUX_GPIO=m +CONFIG_MUX_MMIO=m +CONFIG_COUNTER=m +CONFIG_INTERRUPT_CNT=m +CONFIG_TI_EQEP=m +CONFIG_TI_ECAP_CAPTURE=m +CONFIG_VALIDATE_FS_PARSER=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_JFS_FS=m +CONFIG_JFS_POSIX_ACL=y +CONFIG_JFS_SECURITY=y +CONFIG_GFS2_FS=m +CONFIG_GFS2_FS_LOCKING_DLM=y +CONFIG_BTRFS_FS=y +CONFIG_BTRFS_FS_POSIX_ACL=y +CONFIG_F2FS_FS=y +CONFIG_F2FS_FS_SECURITY=y +CONFIG_FS_ENCRYPTION=y +CONFIG_FS_VERITY=y +CONFIG_FS_VERITY_BUILTIN_SIGNATURES=y +CONFIG_FANOTIFY=y +CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +CONFIG_QFMT_V2=y +CONFIG_AUTOFS4_FS=y +CONFIG_FUSE_FS=y +CONFIG_CUSE=m +CONFIG_VIRTIO_FS=m +CONFIG_OVERLAY_FS=y +CONFIG_FSCACHE=y +CONFIG_FSCACHE_STATS=y +CONFIG_FSCACHE_HISTOGRAM=y +CONFIG_CACHEFILES=y +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_UDF_FS=m +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_IOCHARSET="ascii" +CONFIG_FAT_DEFAULT_UTF8=y +CONFIG_EXFAT_FS=m +CONFIG_NTFS_FS=m +CONFIG_NTFS_RW=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_ECRYPT_FS=m +CONFIG_ECRYPT_FS_MESSAGING=y +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_SUMMARY=y +CONFIG_JFFS2_FS_XATTR=y +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +CONFIG_JFFS2_LZO=y +CONFIG_JFFS2_RUBIN=y +CONFIG_UBIFS_FS=y +CONFIG_UBIFS_FS_ADVANCED_COMPR=y +CONFIG_SQUASHFS=m +CONFIG_SQUASHFS_XATTR=y +CONFIG_SQUASHFS_LZ4=y +CONFIG_SQUASHFS_LZO=y +CONFIG_SQUASHFS_XZ=y +CONFIG_SQUASHFS_ZSTD=y +CONFIG_ROMFS_FS=m +CONFIG_ROMFS_BACKED_BY_BOTH=y +CONFIG_AUFS_FS=m +CONFIG_AUFS_EXPORT=y +CONFIG_AUFS_XATTR=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_NFS_SWAP=y +CONFIG_NFS_V4_1=y +CONFIG_NFS_V4_2=y +CONFIG_ROOT_NFS=y +CONFIG_NFSD=m +CONFIG_NFSD_V3_ACL=y +CONFIG_NFSD_V4=y +CONFIG_NFSD_BLOCKLAYOUT=y +CONFIG_NFSD_V4_SECURITY_LABEL=y +CONFIG_SUNRPC_DEBUG=y +CONFIG_CEPH_FS=m +CONFIG_CEPH_FSCACHE=y +CONFIG_CEPH_FS_POSIX_ACL=y +CONFIG_CIFS=m +CONFIG_CIFS_WEAK_PW_HASH=y +CONFIG_CIFS_UPCALL=y +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +CONFIG_CIFS_DFS_UPCALL=y +CONFIG_CIFS_FSCACHE=y +CONFIG_SMB_SERVER=m +CONFIG_SMB_INSECURE_SERVER=y +CONFIG_CODA_FS=m +CONFIG_AFS_FS=m +CONFIG_AFS_FSCACHE=y +CONFIG_9P_FS=m +CONFIG_9P_FSCACHE=y +CONFIG_9P_FS_POSIX_ACL=y +CONFIG_9P_FS_SECURITY=y +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_CODEPAGE_437=m +CONFIG_NLS_CODEPAGE_737=m +CONFIG_NLS_CODEPAGE_775=m +CONFIG_NLS_CODEPAGE_850=m +CONFIG_NLS_CODEPAGE_852=m +CONFIG_NLS_CODEPAGE_855=m +CONFIG_NLS_CODEPAGE_857=m +CONFIG_NLS_CODEPAGE_860=m +CONFIG_NLS_CODEPAGE_861=m +CONFIG_NLS_CODEPAGE_862=m +CONFIG_NLS_CODEPAGE_863=m +CONFIG_NLS_CODEPAGE_864=m +CONFIG_NLS_CODEPAGE_865=m +CONFIG_NLS_CODEPAGE_866=m +CONFIG_NLS_CODEPAGE_869=m +CONFIG_NLS_CODEPAGE_936=m +CONFIG_NLS_CODEPAGE_950=m +CONFIG_NLS_CODEPAGE_932=m +CONFIG_NLS_CODEPAGE_949=m +CONFIG_NLS_CODEPAGE_874=m +CONFIG_NLS_ISO8859_8=m +CONFIG_NLS_CODEPAGE_1250=m +CONFIG_NLS_CODEPAGE_1251=m +CONFIG_NLS_ASCII=m +CONFIG_NLS_ISO8859_1=m +CONFIG_NLS_ISO8859_2=m +CONFIG_NLS_ISO8859_3=m +CONFIG_NLS_ISO8859_4=m +CONFIG_NLS_ISO8859_5=m +CONFIG_NLS_ISO8859_6=m +CONFIG_NLS_ISO8859_7=m +CONFIG_NLS_ISO8859_9=m +CONFIG_NLS_ISO8859_13=m +CONFIG_NLS_ISO8859_14=m +CONFIG_NLS_ISO8859_15=m +CONFIG_NLS_KOI8_R=m +CONFIG_NLS_KOI8_U=m +CONFIG_NLS_MAC_ROMAN=m +CONFIG_NLS_MAC_CELTIC=m +CONFIG_NLS_MAC_CENTEURO=m +CONFIG_NLS_MAC_CROATIAN=m +CONFIG_NLS_MAC_CYRILLIC=m +CONFIG_NLS_MAC_GAELIC=m +CONFIG_NLS_MAC_GREEK=m +CONFIG_NLS_MAC_ICELAND=m +CONFIG_NLS_MAC_INUIT=m +CONFIG_NLS_MAC_ROMANIAN=m +CONFIG_NLS_MAC_TURKISH=m +CONFIG_DLM=m +CONFIG_DLM_DEBUG=y +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK_XFRM=y +CONFIG_HARDENED_USERCOPY=y +# CONFIG_HARDENED_USERCOPY_FALLBACK is not set +CONFIG_FORTIFY_SOURCE=y +CONFIG_SECURITY_SELINUX=y +CONFIG_SECURITY_TOMOYO=y +CONFIG_SECURITY_APPARMOR=y +CONFIG_SECURITY_YAMA=y +CONFIG_INTEGRITY_SIGNATURE=y +CONFIG_INTEGRITY_ASYMMETRIC_KEYS=y +# CONFIG_INTEGRITY_TRUSTED_KEYRING is not set +CONFIG_DEFAULT_SECURITY_APPARMOR=y +CONFIG_LSM="lockdown,yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo" +CONFIG_INIT_STACK_NONE=y +CONFIG_INIT_ON_ALLOC_DEFAULT_ON=y +CONFIG_CRYPTO_USER=m +CONFIG_CRYPTO_DH=y +CONFIG_CRYPTO_CHACHA20POLY1305=m +CONFIG_CRYPTO_AEGIS128=m +CONFIG_CRYPTO_SEQIV=y +CONFIG_CRYPTO_LRW=m +CONFIG_CRYPTO_ADIANTUM=m +CONFIG_CRYPTO_XCBC=m +CONFIG_CRYPTO_VMAC=m +CONFIG_CRYPTO_RMD128=m +CONFIG_CRYPTO_RMD160=m +CONFIG_CRYPTO_RMD256=m +CONFIG_CRYPTO_RMD320=m +CONFIG_CRYPTO_SHA3=m +CONFIG_CRYPTO_SM3=m +CONFIG_CRYPTO_TGR192=m +CONFIG_CRYPTO_WP512=m +CONFIG_CRYPTO_ANUBIS=m +CONFIG_CRYPTO_ARC4=m +CONFIG_CRYPTO_BLOWFISH=m +CONFIG_CRYPTO_CAMELLIA=m +CONFIG_CRYPTO_CAST5=m +CONFIG_CRYPTO_CAST6=m +CONFIG_CRYPTO_DES=m +CONFIG_CRYPTO_KHAZAD=m +CONFIG_CRYPTO_SALSA20=m +CONFIG_CRYPTO_SEED=m +CONFIG_CRYPTO_SERPENT=m +CONFIG_CRYPTO_TEA=m +CONFIG_CRYPTO_TWOFISH=m +CONFIG_CRYPTO_LZ4=m +CONFIG_CRYPTO_LZ4HC=m +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_CRYPTO_USER_API_HASH=m +CONFIG_CRYPTO_USER_API_SKCIPHER=m +CONFIG_CRYPTO_USER_API_RNG=m +CONFIG_CRYPTO_USER_API_AEAD=m +CONFIG_CRYPTO_DEV_OMAP=y +CONFIG_CRYPTO_DEV_OMAP_SHAM=y +CONFIG_CRYPTO_DEV_OMAP_AES=y +CONFIG_CRYPTO_DEV_OMAP_DES=y +CONFIG_CRYPTO_DEV_ATMEL_ECC=y +CONFIG_CRYPTO_DEV_ATMEL_SHA204A=y +CONFIG_CRYPTO_DEV_VIRTIO=m +# CONFIG_RAID6_PQ_BENCHMARK is not set +CONFIG_CORDIC=m +CONFIG_CRYPTO_LIB_CURVE25519=y +CONFIG_CRYPTO_LIB_CHACHA20POLY1305=y +CONFIG_CRC_ITU_T=y +CONFIG_CRC7=y +# CONFIG_XZ_DEC_X86 is not set +# CONFIG_XZ_DEC_POWERPC is not set +# CONFIG_XZ_DEC_IA64 is not set +# CONFIG_XZ_DEC_SPARC is not set +CONFIG_CMA_SIZE_MBYTES=48 +CONFIG_IRQ_POLL=y +CONFIG_FONTS=y +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +CONFIG_FONT_TER16x32=y +CONFIG_PRINTK_TIME=y +CONFIG_BOOT_PRINTK_DELAY=y +CONFIG_DYNAMIC_DEBUG=y +# CONFIG_DEBUG_BUGVERBOSE is not set +CONFIG_MAGIC_SYSRQ=y +CONFIG_PAGE_EXTENSION=y +CONFIG_PAGE_POISONING=y +CONFIG_SCHED_STACK_END_CHECK=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_SOFTLOCKUP_DETECTOR=y +CONFIG_SCHEDSTATS=y +CONFIG_BUG_ON_DATA_CORRUPTION=y +# CONFIG_STRICT_DEVMEM is not set +# CONFIG_RUNTIME_TESTING_MENU is not set diff --git a/arch/arm/configs/ti_sdk_am3x_release_defconfig b/arch/arm/configs/ti_sdk_am3x_release_defconfig new file mode 100644 index 0000000000000..2040ccba10435 --- /dev/null +++ b/arch/arm/configs/ti_sdk_am3x_release_defconfig @@ -0,0 +1,2083 @@ +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_PERF_EVENTS=y +CONFIG_MACH_ARTPEC6=y +CONFIG_MACH_ASPEED_G6=y +CONFIG_SOC_SAMA5D2=y +CONFIG_SOC_SAMA5D3=y +CONFIG_SOC_SAMA5D4=y +CONFIG_ARCH_BCM_HR2=y +CONFIG_ARCH_BCM2835=y +CONFIG_ARCH_BCM_63XX=y +CONFIG_MACH_BERLIN_BG2=y +CONFIG_MACH_BERLIN_BG2CD=y +CONFIG_MACH_BERLIN_BG2Q=y +CONFIG_SOC_IMX50=y +CONFIG_SOC_IMX51=y +CONFIG_SOC_IMX53=y +CONFIG_SOC_IMX6Q=y +CONFIG_SOC_IMX6SL=y +CONFIG_SOC_IMX6SLL=y +CONFIG_SOC_IMX6SX=y +CONFIG_SOC_IMX6UL=y +CONFIG_SOC_LS1021A=y +CONFIG_SOC_IMX7D=y +CONFIG_SOC_IMX7ULP=y +CONFIG_SOC_VF610=y +CONFIG_ARCH_MILBEAUT_M10V=y +CONFIG_MACH_MMP2_DT=y +CONFIG_MACH_MMP3_DT=y +CONFIG_MACH_ARMADA_370=y +CONFIG_MACH_ARMADA_375=y +CONFIG_MACH_ARMADA_38X=y +CONFIG_MACH_ARMADA_39X=y +CONFIG_MACH_ARMADA_XP=y +CONFIG_MACH_DOVE=y +CONFIG_MACH_SPEAR1310=y +CONFIG_MACH_SPEAR1340=y +CONFIG_ARM_APPENDED_DTB=y +CONFIG_ARM_ATAG_DTB_COMPAT=y +CONFIG_EFI=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y +CONFIG_ARM_IMX6Q_CPUFREQ=y +CONFIG_ARM_RASPBERRYPI_CPUFREQ=y +CONFIG_ARM_ZYNQ_CPUIDLE=y +CONFIG_ARM_EXYNOS_CPUIDLE=y +CONFIG_ARM_TEGRA_CPUIDLE=y +CONFIG_KERNEL_MODE_NEON=y +CONFIG_RASPBERRYPI_FIRMWARE=y +CONFIG_TRUSTED_FOUNDATIONS=y +CONFIG_BCM47XX_NVRAM=y +CONFIG_BCM47XX_SPROM=y +CONFIG_EFI_VARS=m +CONFIG_EFI_CAPSULE_LOADER=m +CONFIG_ARM_CRYPTO=y +CONFIG_CRYPTO_SHA1_ARM_NEON=m +CONFIG_CRYPTO_SHA1_ARM_CE=m +CONFIG_CRYPTO_SHA2_ARM_CE=m +CONFIG_CRYPTO_SHA512_ARM=m +CONFIG_CRYPTO_AES_ARM=m +CONFIG_CRYPTO_AES_ARM_BS=m +CONFIG_CRYPTO_AES_ARM_CE=m +CONFIG_CRYPTO_GHASH_ARM_CE=m +CONFIG_CRYPTO_CRC32_ARM_CE=m +CONFIG_CRYPTO_CHACHA20_NEON=m +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_CMDLINE_PARTITION=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_ESP=m +CONFIG_IPV6_MIP6=m +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_NET_DSA=m +CONFIG_CAN_AT91=m +CONFIG_CAN_FLEXCAN=m +CONFIG_CAN_SUN4I=y +CONFIG_CAN_XILINXCAN=y +CONFIG_CAN_RCAR=m +CONFIG_CAN_MCP251X=y +CONFIG_BT=m +CONFIG_BT_HCIUART=m +CONFIG_BT_HCIUART_BCM=y +CONFIG_BT_MRVL=m +CONFIG_BT_MRVL_SDIO=m +CONFIG_CFG80211=m +CONFIG_MAC80211=m +CONFIG_RFKILL=y +CONFIG_RFKILL_INPUT=y +CONFIG_RFKILL_GPIO=y +CONFIG_NFC=m +CONFIG_NFC_DIGITAL=m +CONFIG_NFC_NCI=m +CONFIG_NFC_NCI_SPI=m +CONFIG_NFC_NCI_UART=m +CONFIG_NFC_HCI=m +CONFIG_NFC_SHDLC=y +CONFIG_NFC_S3FWRN5_I2C=m +CONFIG_PCIEPORTBUS=y +CONFIG_PCI_MVEBU=y +CONFIG_PCI_TEGRA=y +CONFIG_PCI_RCAR_GEN2=y +CONFIG_PCIE_RCAR_HOST=y +CONFIG_OMAP_OCP2SCP=y +CONFIG_SIMPLE_PM_BUS=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_CFI=y +CONFIG_MTD_CFI_INTELEXT=y +CONFIG_MTD_PHYSMAP=y +CONFIG_MTD_PHYSMAP_OF=y +CONFIG_MTD_RAW_NAND=y +CONFIG_MTD_NAND_DENALI_DT=y +CONFIG_MTD_NAND_ATMEL=y +CONFIG_MTD_NAND_MARVELL=y +CONFIG_MTD_NAND_BRCMNAND=y +CONFIG_MTD_NAND_GPMI_NAND=y +CONFIG_MTD_NAND_VF610_NFC=y +CONFIG_MTD_NAND_STM32_FMC2=y +CONFIG_MTD_SPI_NOR=y +CONFIG_SPI_ASPEED_SMC=m +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=65536 +CONFIG_VIRTIO_BLK=y +CONFIG_AD525X_DPOT=y +CONFIG_AD525X_DPOT_I2C=y +CONFIG_ATMEL_TCLIB=y +CONFIG_ICS932S401=y +CONFIG_ATMEL_SSC=m +CONFIG_QCOM_COINCELL=m +CONFIG_APDS9802ALS=y +CONFIG_ISL29003=y +CONFIG_BLK_DEV_SR=y +CONFIG_AHCI_BRCM=y +CONFIG_AHCI_DM816=y +CONFIG_AHCI_ST=y +CONFIG_AHCI_IMX=y +CONFIG_AHCI_SUNXI=y +CONFIG_AHCI_TEGRA=y +CONFIG_SATA_HIGHBANK=y +CONFIG_SATA_MV=y +CONFIG_SATA_RCAR=y +CONFIG_NETDEVICES=y +CONFIG_VIRTIO_NET=y +CONFIG_B53_SPI_DRIVER=m +CONFIG_B53_MDIO_DRIVER=m +CONFIG_B53_MMAP_DRIVER=m +CONFIG_SUN4I_EMAC=y +CONFIG_BGMAC_BCMA=y +CONFIG_MACB=y +CONFIG_NET_CALXEDA_XGMAC=y +CONFIG_FTGMAC100=m +CONFIG_GIANFAR=y +CONFIG_HIX5HD2_GMAC=y +CONFIG_MV643XX_ETH=y +CONFIG_MVNETA=y +CONFIG_PXA168_ETH=m +CONFIG_KS8851=y +CONFIG_R8169=y +CONFIG_SH_ETH=y +CONFIG_SMSC911X=y +CONFIG_SNI_AVE=y +CONFIG_STMMAC_ETH=y +CONFIG_DWMAC_DWC_QOS_ETH=y +CONFIG_XILINX_EMACLITE=y +CONFIG_AT803X_PHY=y +CONFIG_ROCKCHIP_PHY=y +CONFIG_SMSC_PHY=y +CONFIG_BRCMFMAC=m +CONFIG_MWIFIEX=m +CONFIG_MWIFIEX_SDIO=m +CONFIG_RT2X00=m +CONFIG_RT2800USB=m +CONFIG_INPUT_JOYDEV=y +CONFIG_INPUT_EVDEV=y +CONFIG_KEYBOARD_QT1070=m +CONFIG_KEYBOARD_GPIO=y +CONFIG_KEYBOARD_TEGRA=y +CONFIG_KEYBOARD_PXA27x=m +CONFIG_KEYBOARD_SAMSUNG=m +CONFIG_KEYBOARD_ST_KEYSCAN=y +CONFIG_KEYBOARD_SPEAR=y +CONFIG_KEYBOARD_CROS_EC=m +CONFIG_MOUSE_PS2_ELANTECH=y +CONFIG_MOUSE_CYAPA=m +CONFIG_MOUSE_ELAN_I2C=y +CONFIG_TOUCHSCREEN_ADC=m +CONFIG_TOUCHSCREEN_ATMEL_MXT=m +CONFIG_TOUCHSCREEN_ELAN=m +CONFIG_TOUCHSCREEN_MMS114=m +CONFIG_TOUCHSCREEN_WM97XX=m +CONFIG_TOUCHSCREEN_ST1232=m +CONFIG_TOUCHSCREEN_STMPE=y +CONFIG_TOUCHSCREEN_SUN4I=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_MAX77693_HAPTIC=m +CONFIG_INPUT_MAX8997_HAPTIC=m +CONFIG_INPUT_CPCAP_PWRBUTTON=m +CONFIG_INPUT_AXP20X_PEK=m +CONFIG_INPUT_DA9063_ONKEY=m +CONFIG_INPUT_ADXL34X=m +CONFIG_INPUT_STPMIC1_ONKEY=y +CONFIG_SERIO_AMBAKMI=y +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_ASPEED_VUART=m +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_8250_BCM2835AUX=y +CONFIG_SERIAL_8250_MT6577=y +CONFIG_SERIAL_8250_UNIPHIER=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_SERIAL_ATMEL=y +CONFIG_SERIAL_ATMEL_CONSOLE=y +CONFIG_SERIAL_ATMEL_TTYAT=y +CONFIG_SERIAL_MESON=y +CONFIG_SERIAL_MESON_CONSOLE=y +CONFIG_SERIAL_SAMSUNG=y +CONFIG_SERIAL_SAMSUNG_CONSOLE=y +CONFIG_SERIAL_SIRFSOC=y +CONFIG_SERIAL_SIRFSOC_CONSOLE=y +CONFIG_SERIAL_TEGRA=y +CONFIG_SERIAL_IMX=y +CONFIG_SERIAL_IMX_CONSOLE=y +CONFIG_SERIAL_SH_SCI=y +CONFIG_SERIAL_SH_SCI_NR_UARTS=20 +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +CONFIG_SERIAL_VT8500=y +CONFIG_SERIAL_VT8500_CONSOLE=y +CONFIG_SERIAL_OMAP_CONSOLE=y +CONFIG_SERIAL_BCM63XX=y +CONFIG_SERIAL_BCM63XX_CONSOLE=y +CONFIG_SERIAL_XILINX_PS_UART_CONSOLE=y +CONFIG_SERIAL_FSL_LPUART_CONSOLE=y +CONFIG_SERIAL_CONEXANT_DIGICOLOR_CONSOLE=y +CONFIG_SERIAL_ST_ASC_CONSOLE=y +CONFIG_SERIAL_STM32=y +CONFIG_SERIAL_STM32_CONSOLE=y +CONFIG_SERIAL_DEV_BUS=y +CONFIG_VIRTIO_CONSOLE=y +CONFIG_ASPEED_KCS_IPMI_BMC=m +CONFIG_ASPEED_BT_IPMI_BMC=m +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_ST=y +CONFIG_TCG_TPM=m +CONFIG_TCG_TIS_I2C_INFINEON=m +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_ARB_GPIO_CHALLENGE=m +CONFIG_I2C_MUX_PCA954x=y +CONFIG_I2C_MUX_PINCTRL=y +CONFIG_I2C_DEMUX_PINCTRL=y +CONFIG_I2C_ASPEED=m +CONFIG_I2C_AT91=m +CONFIG_I2C_BCM2835=y +CONFIG_I2C_CADENCE=y +CONFIG_I2C_DAVINCI=y +CONFIG_I2C_DIGICOLOR=m +CONFIG_I2C_EMEV2=m +CONFIG_I2C_IMX=y +CONFIG_I2C_MESON=y +CONFIG_I2C_MV64XXX=y +CONFIG_I2C_RIIC=y +CONFIG_I2C_S3C2410=y +CONFIG_I2C_SH_MOBILE=y +CONFIG_I2C_SIRF=y +CONFIG_I2C_ST=y +CONFIG_I2C_STM32F7=y +CONFIG_I2C_SUN6I_P2WI=y +CONFIG_I2C_TEGRA=y +CONFIG_I2C_UNIPHIER=y +CONFIG_I2C_UNIPHIER_F=y +CONFIG_I2C_RCAR=y +CONFIG_I2C_CROS_EC_TUNNEL=m +CONFIG_I2C_SLAVE_EEPROM=y +CONFIG_SPI=y +CONFIG_SPI_ATMEL=m +CONFIG_SPI_BCM2835=y +CONFIG_SPI_BCM2835AUX=y +CONFIG_SPI_DAVINCI=y +CONFIG_SPI_FSL_QUADSPI=m +CONFIG_SPI_GPIO=m +CONFIG_SPI_FSL_DSPI=m +CONFIG_SPI_ORION=y +CONFIG_SPI_PL022=y +CONFIG_SPI_RSPI=y +CONFIG_SPI_S3C64XX=m +CONFIG_SPI_SH_MSIOF=m +CONFIG_SPI_SH_HSPI=y +CONFIG_SPI_SIRF=y +CONFIG_SPI_STM32=m +CONFIG_SPI_STM32_QSPI=y +CONFIG_SPI_SUN4I=y +CONFIG_SPI_SUN6I=y +CONFIG_SPI_TEGRA114=y +CONFIG_SPI_TEGRA20_SFLASH=y +CONFIG_SPI_TEGRA20_SLINK=y +CONFIG_SPMI=y +CONFIG_PINCTRL_AS3722=y +CONFIG_PINCTRL_RZA2=y +CONFIG_PINCTRL_STMFX=y +CONFIG_PINCTRL_PALMAS=y +CONFIG_PINCTRL_APQ8064=y +CONFIG_PINCTRL_APQ8084=y +CONFIG_PINCTRL_IPQ8064=y +CONFIG_PINCTRL_MSM8660=y +CONFIG_PINCTRL_MSM8960=y +CONFIG_PINCTRL_MSM8X74=y +CONFIG_PINCTRL_MSM8916=y +CONFIG_PINCTRL_QCOM_SPMI_PMIC=y +CONFIG_PINCTRL_QCOM_SSBI_PMIC=y +CONFIG_GPIO_ASPEED_SGPIO=y +CONFIG_GPIO_EM=y +CONFIG_GPIO_RCAR=y +CONFIG_GPIO_SYSCON=y +CONFIG_GPIO_UNIPHIER=y +CONFIG_GPIO_ZYNQ=y +CONFIG_GPIO_PALMAS=y +CONFIG_GPIO_TPS65910=y +CONFIG_GPIO_TWL4030=y +CONFIG_GPIO_MXC=y +CONFIG_POWER_AVS=y +CONFIG_ROCKCHIP_IODOMAIN=y +CONFIG_POWER_RESET_AS3722=y +CONFIG_POWER_RESET_GPIO=y +CONFIG_POWER_RESET_GPIO_RESTART=y +CONFIG_POWER_RESET_ST=y +CONFIG_POWER_RESET_KEYSTONE=y +CONFIG_POWER_RESET_RMOBILE=y +CONFIG_BATTERY_ACT8945A=y +CONFIG_BATTERY_CPCAP=m +CONFIG_BATTERY_SBS=y +CONFIG_BATTERY_BQ27XXX=m +CONFIG_AXP20X_POWER=m +CONFIG_BATTERY_MAX17040=m +CONFIG_BATTERY_MAX17042=m +CONFIG_CHARGER_CPCAP=m +CONFIG_CHARGER_GPIO=m +CONFIG_CHARGER_MAX14577=m +CONFIG_CHARGER_MAX77693=m +CONFIG_CHARGER_MAX8997=m +CONFIG_CHARGER_MAX8998=m +CONFIG_CHARGER_TPS65090=y +CONFIG_SENSORS_ASPEED=m +CONFIG_SENSORS_IIO_HWMON=y +CONFIG_SENSORS_LM90=y +CONFIG_SENSORS_LM95245=y +CONFIG_SENSORS_NTC_THERMISTOR=m +CONFIG_SENSORS_PWM_FAN=m +CONFIG_SENSORS_RASPBERRYPI_HWMON=m +CONFIG_SENSORS_INA2XX=m +CONFIG_CPU_THERMAL=y +CONFIG_DEVFREQ_THERMAL=y +CONFIG_IMX_THERMAL=y +CONFIG_ROCKCHIP_THERMAL=y +CONFIG_RCAR_THERMAL=y +CONFIG_ARMADA_THERMAL=y +CONFIG_BCM2711_THERMAL=m +CONFIG_BCM2835_THERMAL=m +CONFIG_BRCMSTB_THERMAL=m +CONFIG_ST_THERMAL_MEMMAP=y +CONFIG_UNIPHIER_THERMAL=y +CONFIG_DA9063_WATCHDOG=m +CONFIG_ARM_SP805_WATCHDOG=y +CONFIG_AT91SAM9X_WATCHDOG=y +CONFIG_SAMA5D4_WATCHDOG=y +CONFIG_S3C2410_WATCHDOG=m +CONFIG_DAVINCI_WATCHDOG=m +CONFIG_ORION_WATCHDOG=y +CONFIG_RN5T618_WATCHDOG=y +CONFIG_SUNXI_WATCHDOG=y +CONFIG_IMX2_WDT=y +CONFIG_ST_LPC_WATCHDOG=y +CONFIG_TEGRA_WATCHDOG=m +CONFIG_MESON_WATCHDOG=y +CONFIG_DIGICOLOR_WATCHDOG=y +CONFIG_RENESAS_WDT=m +CONFIG_RENESAS_RZAWDT=m +CONFIG_STPMIC1_WATCHDOG=y +CONFIG_BCM47XX_WDT=y +CONFIG_BCM2835_WDT=y +CONFIG_BCM_KONA_WDT=y +CONFIG_BCM7038_WDT=m +CONFIG_BCMA_HOST_SOC=y +CONFIG_BCMA_DRIVER_GMAC_CMN=y +CONFIG_BCMA_DRIVER_GPIO=y +CONFIG_MFD_ACT8945A=y +CONFIG_MFD_AC100=y +CONFIG_MFD_AXP20X_RSB=y +CONFIG_MFD_CPCAP=y +CONFIG_MFD_QCOM_RPM=y +CONFIG_MFD_SPMI_PMIC=y +CONFIG_MFD_PALMAS=y +CONFIG_MFD_TPS65090=y +CONFIG_MFD_TPS65217=y +CONFIG_MFD_TPS65218=y +CONFIG_MFD_TPS6586X=y +CONFIG_MFD_TPS65910=y +CONFIG_MFD_STM32_LPTIMER=m +CONFIG_MFD_STPMIC1=y +CONFIG_REGULATOR_ACT8945A=y +CONFIG_REGULATOR_ANATOP=y +CONFIG_REGULATOR_AB8500=y +CONFIG_REGULATOR_AS3711=y +CONFIG_REGULATOR_AS3722=y +CONFIG_REGULATOR_AXP20X=y +CONFIG_REGULATOR_BCM590XX=y +CONFIG_REGULATOR_CPCAP=y +CONFIG_REGULATOR_GPIO=y +CONFIG_REGULATOR_MAX14577=m +CONFIG_REGULATOR_MAX8907=y +CONFIG_REGULATOR_MAX8997=m +CONFIG_REGULATOR_MAX8998=m +CONFIG_REGULATOR_MAX77686=y +CONFIG_REGULATOR_MAX77693=m +CONFIG_REGULATOR_MAX77802=y +CONFIG_REGULATOR_PALMAS=y +CONFIG_REGULATOR_PBIAS=y +CONFIG_REGULATOR_QCOM_RPM=y +CONFIG_REGULATOR_QCOM_SMD_RPM=m +CONFIG_REGULATOR_RK808=y +CONFIG_REGULATOR_RN5T618=y +CONFIG_REGULATOR_S2MPA01=m +CONFIG_REGULATOR_S2MPS11=y +CONFIG_REGULATOR_S5M8767=y +CONFIG_REGULATOR_STM32_BOOSTER=m +CONFIG_REGULATOR_STM32_VREFBUF=m +CONFIG_REGULATOR_STM32_PWR=y +CONFIG_REGULATOR_STPMIC1=y +CONFIG_REGULATOR_TI_ABB=y +CONFIG_REGULATOR_TPS62360=y +CONFIG_REGULATOR_TPS65090=y +CONFIG_REGULATOR_TPS65217=y +CONFIG_REGULATOR_TPS65218=y +CONFIG_REGULATOR_TPS6586X=y +CONFIG_REGULATOR_TPS65910=y +CONFIG_REGULATOR_VEXPRESS=y +CONFIG_REGULATOR_WM8994=m +CONFIG_MEDIA_CEC_SUPPORT=y +CONFIG_VIDEO_MMP_CAMERA=m +CONFIG_VIDEO_ASPEED=m +CONFIG_VIDEO_STM32_DCMI=m +CONFIG_VIDEO_RENESAS_CEU=m +CONFIG_VIDEO_SAMSUNG_EXYNOS4_IS=m +CONFIG_VIDEO_S5P_FIMC=m +CONFIG_VIDEO_S5P_MIPI_CSIS=m +CONFIG_VIDEO_EXYNOS_FIMC_LITE=m +CONFIG_VIDEO_EXYNOS4_FIMC_IS=m +CONFIG_VIDEO_RCAR_VIN=m +CONFIG_VIDEO_ATMEL_ISI=m +CONFIG_VIDEO_SAMSUNG_S5P_JPEG=m +CONFIG_VIDEO_SAMSUNG_S5P_MFC=m +CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC=m +CONFIG_VIDEO_STI_BDISP=m +CONFIG_VIDEO_STI_HVA=m +CONFIG_VIDEO_STI_DELTA=m +CONFIG_VIDEO_RENESAS_FDP1=m +CONFIG_VIDEO_RENESAS_JPU=m +CONFIG_VIDEO_RENESAS_VSP1=m +CONFIG_V4L_TEST_DRIVERS=y +CONFIG_VIDEO_VIVID=m +CONFIG_CEC_PLATFORM_DRIVERS=y +CONFIG_CEC_SAMSUNG_S5P=m +CONFIG_VIDEO_ADV7180=m +CONFIG_VIDEO_ADV7604=m +CONFIG_VIDEO_ADV7604_CEC=y +CONFIG_VIDEO_ML86V7667=m +CONFIG_IMX_IPUV3_CORE=m +# CONFIG_DRM_I2C_CH7006 is not set +# CONFIG_DRM_I2C_SIL164 is not set +CONFIG_DRM_EXYNOS_FIMD=y +CONFIG_DRM_EXYNOS_MIXER=y +CONFIG_DRM_EXYNOS_DPI=y +CONFIG_DRM_EXYNOS_DSI=y +CONFIG_DRM_EXYNOS_HDMI=y +CONFIG_DRM_ROCKCHIP=m +CONFIG_ROCKCHIP_ANALOGIX_DP=y +CONFIG_ROCKCHIP_DW_HDMI=y +CONFIG_ROCKCHIP_DW_MIPI_DSI=y +CONFIG_ROCKCHIP_INNO_HDMI=y +CONFIG_DRM_IMX=m +CONFIG_DRM_IMX_PARALLEL_DISPLAY=m +CONFIG_DRM_IMX_TVE=m +CONFIG_DRM_IMX_LDB=m +CONFIG_DRM_IMX_HDMI=m +CONFIG_DRM_RCAR_DU=m +CONFIG_DRM_SUN4I=m +CONFIG_DRM_MSM=m +CONFIG_DRM_TEGRA=y +CONFIG_DRM_STM_DSI=m +CONFIG_DRM_SIMPLE_BRIDGE=m +CONFIG_DRM_I2C_ADV7511_AUDIO=y +CONFIG_DRM_VC4=m +CONFIG_DRM_ASPEED_GFX=m +CONFIG_FB_WM8505=y +CONFIG_FB_SH_MOBILE_LCDC=y +CONFIG_BACKLIGHT_AS3711=y +CONFIG_SND_HDA_TEGRA=m +CONFIG_SND_HDA_INPUT_BEEP=y +CONFIG_SND_HDA_PATCH_LOADER=y +CONFIG_SND_HDA_CODEC_REALTEK=m +CONFIG_SND_HDA_CODEC_HDMI=m +CONFIG_SND_ATMEL_SOC_WM8904=m +CONFIG_SND_ATMEL_SOC_PDMIC=m +CONFIG_SND_ATMEL_SOC_I2S=m +CONFIG_SND_BCM2835_SOC_I2S=m +CONFIG_SND_MMP_SOC=y +CONFIG_SND_PXA_SOC_SSP=m +CONFIG_SND_PXA910_SOC=m +CONFIG_SND_SOC_ROCKCHIP=m +CONFIG_SND_SOC_ROCKCHIP_SPDIF=m +CONFIG_SND_SOC_ROCKCHIP_MAX98090=m +CONFIG_SND_SOC_ROCKCHIP_RT5645=m +CONFIG_SND_SOC_SAMSUNG=m +CONFIG_SND_SOC_SAMSUNG_SMDK_WM8994=m +CONFIG_SND_SOC_SMDK_WM8994_PCM=m +CONFIG_SND_SOC_SNOW=m +CONFIG_SND_SOC_ODROID=m +CONFIG_SND_SOC_ARNDALE=m +CONFIG_SND_SOC_SH4_FSI=m +CONFIG_SND_SOC_RCAR=m +CONFIG_SND_SOC_STI=m +CONFIG_SND_SOC_STM32_SAI=m +CONFIG_SND_SOC_STM32_I2S=m +CONFIG_SND_SUN4I_CODEC=m +CONFIG_SND_SOC_TEGRA=m +CONFIG_SND_SOC_TEGRA20_I2S=m +CONFIG_SND_SOC_TEGRA30_I2S=m +CONFIG_SND_SOC_TEGRA_RT5640=m +CONFIG_SND_SOC_TEGRA_WM8753=m +CONFIG_SND_SOC_TEGRA_WM8903=m +CONFIG_SND_SOC_TEGRA_WM9712=m +CONFIG_SND_SOC_TEGRA_TRIMSLICE=m +CONFIG_SND_SOC_TEGRA_ALC5632=m +CONFIG_SND_SOC_TEGRA_MAX98090=m +CONFIG_SND_SOC_CS42L51_I2C=m +CONFIG_SND_SOC_SPDIF=m +CONFIG_USB_OTG=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_MVEBU=y +CONFIG_USB_XHCI_TEGRA=m +CONFIG_USB_EHCI_HCD_STI=y +CONFIG_USB_EHCI_TEGRA=y +CONFIG_USB_EHCI_EXYNOS=y +CONFIG_USB_EHCI_MV=m +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_STI=y +CONFIG_USB_OHCI_EXYNOS=m +CONFIG_USB_R8A66597_HCD=m +CONFIG_USB_RENESAS_USBHS=m +CONFIG_USB_STORAGE=y +CONFIG_USB_UAS=m +CONFIG_USB_MUSB_SUNXI=m +CONFIG_USB_MUSB_TUSB6010=m +CONFIG_USB_MUSB_UX500=m +CONFIG_USB_UX500_DMA=y +CONFIG_USB_INVENTRA_DMA=y +CONFIG_USB_TUSB_OMAP_DMA=y +CONFIG_USB_CHIPIDEA_UDC=y +CONFIG_USB_CHIPIDEA_HOST=y +CONFIG_USB_ISP1760=y +CONFIG_USB_HSIC_USB3503=y +CONFIG_AB8500_USB=y +CONFIG_KEYSTONE_USB_PHY=m +CONFIG_TWL6030_USB=m +CONFIG_USB_GPIO_VBUS=y +CONFIG_USB_ISP1301=y +CONFIG_USB_MXS_PHY=y +CONFIG_USB_FSL_USB2=y +CONFIG_USB_RENESAS_USBHS_UDC=m +CONFIG_USB_ASPEED_VHUB=m +CONFIG_USB_CONFIGFS_F_UAC1_LEGACY=y +CONFIG_MMC_BLOCK_MINORS=16 +CONFIG_MMC_ARMMMCI=y +CONFIG_MMC_SDHCI_OF_ARASAN=y +CONFIG_MMC_SDHCI_OF_AT91=y +CONFIG_MMC_SDHCI_OF_ESDHC=y +CONFIG_MMC_SDHCI_ESDHC_IMX=y +CONFIG_MMC_SDHCI_DOVE=y +CONFIG_MMC_SDHCI_TEGRA=y +CONFIG_MMC_SDHCI_S3C=y +CONFIG_MMC_SDHCI_PXAV3=y +CONFIG_MMC_SDHCI_PXAV2=m +CONFIG_MMC_SDHCI_SPEAR=y +CONFIG_MMC_SDHCI_S3C_DMA=y +CONFIG_MMC_SDHCI_BCM_KONA=y +CONFIG_MMC_MESON_MX_SDIO=y +CONFIG_MMC_SDHCI_ST=y +CONFIG_MMC_OMAP=y +CONFIG_MMC_ATMELMCI=y +CONFIG_MMC_SDHCI_MSM=y +CONFIG_MMC_MVSDIO=y +CONFIG_MMC_SDHI=y +CONFIG_MMC_UNIPHIER=y +CONFIG_MMC_DW_EXYNOS=y +CONFIG_MMC_DW_ROCKCHIP=y +CONFIG_MMC_SH_MMCIF=y +CONFIG_MMC_SUNXI=y +CONFIG_MMC_BCM2835=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS_FLASH=m +CONFIG_LEDS_CPCAP=m +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_PWM=y +CONFIG_LEDS_MAX77693=m +CONFIG_LEDS_MAX8997=m +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_ONESHOT=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_BACKLIGHT=y +CONFIG_LEDS_TRIGGER_CPU=y +CONFIG_LEDS_TRIGGER_GPIO=y +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y +CONFIG_LEDS_TRIGGER_TRANSIENT=y +CONFIG_LEDS_TRIGGER_CAMERA=y +CONFIG_EDAC=y +CONFIG_EDAC_HIGHBANK_MC=y +CONFIG_EDAC_HIGHBANK_L2=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_AC100=y +CONFIG_RTC_DRV_AS3722=y +CONFIG_RTC_DRV_MAX8907=y +CONFIG_RTC_DRV_MAX8998=m +CONFIG_RTC_DRV_MAX8997=m +CONFIG_RTC_DRV_MAX77686=y +CONFIG_RTC_DRV_RK808=m +CONFIG_RTC_DRV_PCF85363=m +CONFIG_RTC_DRV_TWL4030=y +CONFIG_RTC_DRV_S5M=m +CONFIG_RTC_DRV_DA9063=m +CONFIG_RTC_DRV_EFI=m +CONFIG_RTC_DRV_DIGICOLOR=m +CONFIG_RTC_DRV_S3C=m +CONFIG_RTC_DRV_SA1100=m +CONFIG_RTC_DRV_SH=m +CONFIG_RTC_DRV_PL031=y +CONFIG_RTC_DRV_AT91RM9200=m +CONFIG_RTC_DRV_AT91SAM9=m +CONFIG_RTC_DRV_VT8500=y +CONFIG_RTC_DRV_SUNXI=y +CONFIG_RTC_DRV_MV=y +CONFIG_RTC_DRV_TEGRA=y +CONFIG_RTC_DRV_ST_LPC=y +CONFIG_RTC_DRV_STM32=y +CONFIG_RTC_DRV_CPCAP=m +CONFIG_RTC_DRV_ASPEED=m +CONFIG_DMADEVICES=y +CONFIG_AT_HDMAC=y +CONFIG_AT_XDMAC=y +CONFIG_DMA_BCM2835=y +CONFIG_DMA_SUN6I=y +CONFIG_FSL_EDMA=y +CONFIG_IMX_DMA=y +CONFIG_IMX_SDMA=y +CONFIG_MV_XOR=y +CONFIG_MXS_DMA=y +CONFIG_PL330_DMA=y +CONFIG_SIRF_DMA=y +CONFIG_STE_DMA40=y +CONFIG_ST_FDMA=m +CONFIG_STM32_DMA=y +CONFIG_STM32_DMAMUX=y +CONFIG_STM32_MDMA=y +CONFIG_TEGRA20_APB_DMA=y +CONFIG_UNIPHIER_MDMAC=y +CONFIG_XILINX_DMA=y +CONFIG_QCOM_BAM_DMA=y +CONFIG_DW_DMAC=y +CONFIG_RCAR_DMAC=y +CONFIG_RENESAS_USB_DMAC=m +CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO_MMIO=y +CONFIG_MFD_NVEC=y +CONFIG_KEYBOARD_NVEC=y +CONFIG_SERIO_NVEC_PS2=y +CONFIG_NVEC_POWER=y +CONFIG_NVEC_PAZ00=y +CONFIG_STAGING_BOARD=y +CONFIG_MFD_CROS_EC_DEV=m +CONFIG_CROS_EC_I2C=m +CONFIG_CROS_EC_SPI=m +CONFIG_COMMON_CLK_MAX77686=y +CONFIG_COMMON_CLK_RK808=m +CONFIG_COMMON_CLK_S2MPS11=m +CONFIG_CLK_RASPBERRYPI=y +CONFIG_COMMON_CLK_QCOM=y +CONFIG_QCOM_CLK_RPM=y +CONFIG_APQ_MMCC_8084=y +CONFIG_MSM_GCC_8660=y +CONFIG_MSM_MMCC_8960=y +CONFIG_MSM_MMCC_8974=y +CONFIG_BCM2835_MBOX=y +CONFIG_ROCKCHIP_IOMMU=y +CONFIG_TEGRA_IOMMU_GART=y +CONFIG_TEGRA_IOMMU_SMMU=y +CONFIG_EXYNOS_IOMMU=y +CONFIG_REMOTEPROC=y +CONFIG_ST_REMOTEPROC=m +CONFIG_ASPEED_LPC_CTRL=m +CONFIG_ASPEED_LPC_SNOOP=m +CONFIG_ASPEED_P2A_CTRL=m +CONFIG_RASPBERRYPI_POWER=y +CONFIG_QCOM_GSBI=y +CONFIG_QCOM_PM=y +CONFIG_QCOM_SMD_RPM=m +CONFIG_QCOM_WCNSS_CTRL=m +CONFIG_ARCH_R7S9210=y +CONFIG_ARCH_R8A7742=y +CONFIG_ARCH_R8A7743=y +CONFIG_ARCH_R8A7744=y +CONFIG_ARCH_R8A7745=y +CONFIG_ARCH_R8A77470=y +CONFIG_ARCH_R8A7792=y +CONFIG_ARCH_R9A06G032=y +CONFIG_ROCKCHIP_PM_DOMAINS=y +CONFIG_ARM_EXYNOS_BUS_DEVFREQ=m +CONFIG_ARM_TEGRA_DEVFREQ=m +CONFIG_DEVFREQ_EVENT_EXYNOS_NOCP=m +CONFIG_EXTCON_MAX14577=m +CONFIG_EXTCON_MAX77693=m +CONFIG_EXTCON_MAX8997=m +CONFIG_STM32_FMC2_EBI=y +CONFIG_EXYNOS5422_DMC=m +CONFIG_IIO=y +CONFIG_IIO_SW_TRIGGER=y +CONFIG_ASPEED_ADC=m +CONFIG_AT91_ADC=m +CONFIG_AT91_SAMA5D2_ADC=m +CONFIG_BERLIN2_ADC=m +CONFIG_CPCAP_ADC=m +CONFIG_EXYNOS_ADC=m +CONFIG_MESON_SARADC=m +CONFIG_ROCKCHIP_SARADC=m +CONFIG_STM32_ADC_CORE=m +CONFIG_STM32_ADC=m +CONFIG_STM32_DFSDM_ADC=m +CONFIG_VF610_ADC=m +CONFIG_XILINX_XADC=y +CONFIG_IIO_CROS_EC_SENSORS_CORE=m +CONFIG_IIO_CROS_EC_SENSORS=m +CONFIG_STM32_DAC=m +CONFIG_MPU3050_I2C=y +CONFIG_CM36651=m +CONFIG_IIO_CROS_EC_LIGHT_PROX=m +CONFIG_SENSORS_ISL29018=y +CONFIG_SENSORS_ISL29028=y +CONFIG_AK8975=y +CONFIG_IIO_HRTIMER_TRIGGER=y +CONFIG_IIO_STM32_LPTIMER_TRIGGER=m +CONFIG_PWM=y +CONFIG_PWM_ATMEL=m +CONFIG_PWM_ATMEL_HLCDC_PWM=m +CONFIG_PWM_ATMEL_TCB=m +CONFIG_PWM_BCM2835=y +CONFIG_PWM_BRCMSTB=m +CONFIG_PWM_FSL_FTM=m +CONFIG_PWM_MESON=m +CONFIG_PWM_RCAR=m +CONFIG_PWM_RENESAS_TPU=y +CONFIG_PWM_ROCKCHIP=m +CONFIG_PWM_SAMSUNG=m +CONFIG_PWM_STI=y +CONFIG_PWM_STM32=m +CONFIG_PWM_STM32_LP=m +CONFIG_PWM_SUN4I=y +CONFIG_PWM_TEGRA=y +CONFIG_PWM_VT8500=y +CONFIG_KEYSTONE_IRQ=y +CONFIG_PHY_SUN4I_USB=y +CONFIG_PHY_SUN9I_USB=y +CONFIG_PHY_HIX5HD2_SATA=y +CONFIG_PHY_BERLIN_SATA=y +CONFIG_PHY_BERLIN_USB=y +CONFIG_PHY_MMP3_USB=m +CONFIG_PHY_CPCAP_USB=m +CONFIG_PHY_QCOM_APQ8064_SATA=m +CONFIG_PHY_RCAR_GEN2=m +CONFIG_PHY_ROCKCHIP_DP=m +CONFIG_PHY_ROCKCHIP_USB=y +CONFIG_PHY_SAMSUNG_USB2=m +CONFIG_PHY_EXYNOS5250_SATA=m +CONFIG_PHY_UNIPHIER_USB2=y +CONFIG_PHY_UNIPHIER_USB3=y +CONFIG_PHY_MIPHY28LP=y +CONFIG_PHY_STIH407_USB=y +CONFIG_PHY_STM32_USBPHYC=y +CONFIG_PHY_TEGRA_XUSB=y +CONFIG_PHY_DM816X_USB=m +CONFIG_TI_PIPE3=y +CONFIG_TWL4030_USB=m +CONFIG_NVMEM_IMX_OCOTP=y +CONFIG_ROCKCHIP_EFUSE=m +CONFIG_NVMEM_SUNXI_SID=y +CONFIG_NVMEM_VF610_OCOTP=y +CONFIG_MESON_MX_EFUSE=m +CONFIG_FSI=m +CONFIG_FSI_MASTER_GPIO=m +CONFIG_FSI_MASTER_HUB=m +CONFIG_FSI_MASTER_ASPEED=m +CONFIG_FSI_SCOM=m +CONFIG_FSI_SBEFIFO=m +CONFIG_FSI_OCC=m +CONFIG_EXT4_FS=y +CONFIG_AUTOFS4_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_NTFS_FS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_SQUASHFS=y +CONFIG_SQUASHFS_LZO=y +CONFIG_SQUASHFS_XZ=y +CONFIG_PSTORE=y +CONFIG_PSTORE_CONSOLE=y +CONFIG_PSTORE_PMSG=y +CONFIG_PSTORE_RAM=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_NFS_V4_1=y +CONFIG_NFS_V4_2=y +CONFIG_ROOT_NFS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_UTF8=y +CONFIG_CRYPTO_USER=m +CONFIG_CRYPTO_USER_API_HASH=m +CONFIG_CRYPTO_USER_API_SKCIPHER=m +CONFIG_CRYPTO_USER_API_RNG=m +CONFIG_CRYPTO_USER_API_AEAD=m +CONFIG_CRYPTO_DEV_SUN4I_SS=m +CONFIG_CRYPTO_DEV_FSL_CAAM=m +CONFIG_CRYPTO_DEV_MARVELL_CESA=m +CONFIG_CRYPTO_DEV_EXYNOS_RNG=m +CONFIG_CRYPTO_DEV_S5P=m +CONFIG_CRYPTO_DEV_ATMEL_AES=m +CONFIG_CRYPTO_DEV_ATMEL_TDES=m +CONFIG_CRYPTO_DEV_ATMEL_SHA=m +CONFIG_CRYPTO_DEV_ROCKCHIP=m +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_CROS_EC=m +CONFIG_CROS_EC_CHARDEV=m +# Add config flags here that only appear in the multi_v7_defconfig +CONFIG_ARCH_ALPINE=n +CONFIG_ARCH_ARTPEC=n +CONFIG_ARCH_ASPEED=n +CONFIG_ARCH_AT91=n +CONFIG_ARCH_BCM=n +CONFIG_ARCH_BCM_CYGNUS=n +CONFIG_ARCH_BCM_NSP=n +CONFIG_ARCH_BCM_5301X=n +CONFIG_ARCH_BCM_281XX=n +CONFIG_ARCH_BCM_21664=n +CONFIG_ARCH_BRCMSTB=n +CONFIG_ARCH_BERLIN=n +CONFIG_ARCH_DIGICOLOR=n +CONFIG_ARCH_HIGHBANK=n +CONFIG_ARCH_HISI=n +CONFIG_ARCH_HI3xxx=n +CONFIG_ARCH_HIP01=n +CONFIG_ARCH_HIP04=n +CONFIG_ARCH_HIX5HD2=n +CONFIG_ARCH_MESON=n +CONFIG_ARCH_MXC=n +CONFIG_ARCH_MEDIATEK=n +CONFIG_ARCH_MILBEAUT=n +CONFIG_ARCH_QCOM=n +CONFIG_ARCH_MMP=n +CONFIG_ARCH_MSM8X60=n +CONFIG_ARCH_MSM8960=n +CONFIG_ARCH_MSM8974=n +CONFIG_ARCH_ROCKCHIP=n +CONFIG_ARCH_SOCFPGA=n +CONFIG_ARCH_SPEAR13XX=n +CONFIG_ARCH_STI=n +CONFIG_ARCH_EXYNOS=n +CONFIG_ARCH_SHMOBILE_MULTI=n +CONFIG_ARCH_EMEV2=n +CONFIG_ARCH_R7S72100=n +CONFIG_ARCH_R8A73A4=n +CONFIG_ARCH_R8A7740=n +CONFIG_ARCH_R8A7778=n +CONFIG_ARCH_R8A7779=n +CONFIG_ARCH_R8A7790=n +CONFIG_ARCH_R8A7791=n +CONFIG_ARCH_R8A7793=n +CONFIG_ARCH_R8A7794=n +CONFIG_ARCH_RENESAS=n +CONFIG_ARCH_SH73A0=n +CONFIG_ARCH_SUNXI=n +CONFIG_ARCH_STM32=n +CONFIG_ARCH_SIRF=n +CONFIG_ARCH_TEGRA=n +CONFIG_ARCH_TEGRA_2x_SOC=n +CONFIG_ARCH_TEGRA_3x_SOC=n +CONFIG_ARCH_TEGRA_114_SOC=n +CONFIG_ARCH_TEGRA_124_SOC=n +CONFIG_ARCH_UNIPHIER=n +CONFIG_ARCH_U8500=n +CONFIG_ARCH_VEXPRESS=n +CONFIG_ARCH_VEXPRESS_TC2_PM=n +CONFIG_ARCH_WM8850=n +CONFIG_ARCH_ZYNQ=n +CONFIG_ARCH_VIRT=n +CONFIG_ARCH_MVEBU=n +CONFIG_PLAT_SPEAR=n +CONFIG_CHROME_PLATFORMS=n +CONFIG_ARCH_OMAP2=n +CONFIG_ARCH_OMAP3=n +CONFIG_ARCH_OMAP4=n +CONFIG_ARCH_KEYSTONE=n + +# Multifunction device drivers +CONFIG_MFD_AS3711=n +CONFIG_MFD_AS3722=n +CONFIG_MFD_ATMEL_FLEXCOM=n +CONFIG_MFD_ATMEL_HLCDC=n +CONFIG_MFD_BCM590XX=n +CONFIG_MFD_AXP20X_I2C=n +CONFIG_MFD_DA9063=n +CONFIG_MFD_MAX14577=n +CONFIG_MFD_MAX77686=n +CONFIG_MFD_MAX77693=n +CONFIG_MFD_MAX8907=n +CONFIG_MFD_MAX8997=n +CONFIG_MFD_MAX8998=n +CONFIG_MFD_PM8XXX=n +CONFIG_MFD_RK808=n +CONFIG_MFD_RN5T618=n +CONFIG_MFD_SEC_CORE=n +CONFIG_MFD_STMPE=n + +# Regulators +CONFIG_REGULATOR_ACT8865=n +CONFIG_REGULATOR_DA9210=n +CONFIG_REGULATOR_FAN53555=n +CONFIG_REGULATOR_LP872X=n +CONFIG_REGULATOR_MAX8952=n +CONFIG_REGULATOR_MAX8973=n +CONFIG_REGULATOR_PWM=n +CONFIG_REGULATOR_TPS51632=n +CONFIG_REGULATOR_TWL4030=n + +# RTC drivers +CONFIG_RTC_DRV_HYM8563=n +CONFIG_RTC_DRV_RS5C372=n +CONFIG_RTC_DRV_BQ32K=n +CONFIG_RTC_DRV_S35390A=n +CONFIG_RTC_DRV_RX8581=n +CONFIG_RTC_DRV_EM3027=n + +# Watchdog Device Drivers +CONFIG_XILINX_WATCHDOG=n +CONFIG_DW_WATCHDOG=n + +# GPIO +CONFIG_GPIO_DWAPB=n +CONFIG_GPIO_XILINX=n +CONFIG_GPIO_TPS6586X=n + +# Sound +CONFIG_SND_ARM=n +CONFIG_SND_SPI=n +CONFIG_SND_ATMEL_SOC=n +CONFIG_SND_SOC_FSL_SAI=n +CONFIG_SND_SOC_AK4642=n +CONFIG_SND_SOC_CPCAP=n +CONFIG_SND_SOC_SGTL5000=n +CONFIG_SND_SOC_STI_SAS=n +CONFIG_SND_SOC_WM8978=n + +# DRM +CONFIG_VGA_ARB=n +CONFIG_DRM_I2C_ADV7511=n +CONFIG_DRM_NOUVEAU=n +CONFIG_DRM_EXYNOS=n +CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0=n +CONFIG_DRM_NXP_PTN3460=n +CONFIG_DRM_PARADE_PS8622=n +CONFIG_DRM_STI=n +CONFIG_DRM_PANEL_SAMSUNG_LD9040=n +CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03=n +CONFIG_DRM_DUMB_VGA_DAC=n +CONFIG_DRM_HISI_KIRIN=n +CONFIG_DRM_RCAR_LVDS=n +CONFIG_DRM_FSL_DCU=n +CONFIG_DRM_SII9234=n +CONFIG_DRM_MXSFB=n +CONFIG_DRM_HISI_HIBMC=n +CONFIG_DRM_ATMEL_HLCDC=n +CONFIG_DRM_STM=n +CONFIG_DRM_PANEL_ORISETECH_OTM8009A=n +CONFIG_DRM_PANEL_RAYDIUM_RM68200=n +CONFIG_DRM_TOSHIBA_TC358764=n +CONFIG_DRM_ETNAVIV=n +CONFIG_DRM_PL111=n +CONFIG_DRM_LIMA=n +CONFIG_DRM_PANFROST=n + +# Video +CONFIG_LCD_CLASS_DEVICE=n +CONFIG_FB_EFI=n +CONFIG_FB_SIMPLE=n +CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=n +CONFIG_FB_ARMCLCD=n +CONFIG_XEN_FBDEV_FRONTEND=n +################################################## +# TI Baseport Config Options +################################################## + +# Kernel compression +CONFIG_KERNEL_GZIP=n +CONFIG_KERNEL_LZMA=y +CONFIG_KERNEL_LZ4=n +CONFIG_KERNEL_XZ=n +CONFIG_KERNEL_LZO=n + +# Enable process accounting +CONFIG_BSD_PROCESS_ACCT=y + +# Have some way to pick up kernel config later on +# Always useful to look at /proc/config.gz +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y + +# Add Bin2c +CONFIG_BUILD_BIN2C=y + +# Add base Cgroups functions +CONFIG_MEMCG=y + +# Choose CONFIG_EMBEDDED +CONFIG_EMBEDDED=y + +# Enable all kernel symbols please +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y + +# Enable AEABI +CONFIG_AEABI=y + +# How do we want kernel Modules to work? +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SRCVERSION_ALL=y + +# POSIX Message queue +CONFIG_POSIX_MQUEUE=y + +# No Multi Cluster systems in TI yet.. +CONFIG_MCPM=n + +# Serial +CONFIG_SERIAL_8250_OMAP=y +CONFIG_SERIAL_8250_OMAP_TTYO_FIXUP=y +CONFIG_SERIAL_8250_NR_UARTS=10 +CONFIG_SERIAL_8250_RUNTIME_UARTS=10 +CONFIG_SERIAL_8250_DMA=y +CONFIG_SERIAL_OMAP=n +CONFIG_SERIAL_8250_DW=n +CONFIG_SERIAL_8250_EM=n +CONFIG_SERIAL_AMBA_PL011=n +CONFIG_SERIAL_XILINX_PS_UART=n +CONFIG_SERIAL_FSL_LPUART=n +CONFIG_SERIAL_CONEXANT_DIGICOLOR=n +CONFIG_SERIAL_ST_ASC=n + +CONFIG_JUMP_LABEL=y + +# Disable Extra debug options +CONFIG_FTRACE=n +CONFIG_DEBUG_PREEMPT=n +CONFIG_SLUB_DEBUG=n +CONFIG_DEBUG_BUGVERBOSE=n + +CONFIG_PREEMPT=y +CONFIG_DEBUG_FS=y + +# Enable System V IPC +CONFIG_SYSVIPC=y + +# Power management options +CONFIG_PM_DEBUG=y + +# Clock framework stuff we need +CONFIG_COMMON_CLK_PALMAS=y +CONFIG_OMAP_RESET_CLOCKS=y + +# CPU Idle +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_CPU_IDLE_GOV_MENU=y +CONFIG_DT_IDLE_STATES=y + +# ARM CPU Idle Drivers +CONFIG_ARM_CPUIDLE=y +CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED=n + +# CPU Frequency scaling +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y + +# CPUFreq Driver Options +CONFIG_CPUFREQ_DT=y +CONFIG_ARM_BIG_LITTLE_CPUFREQ=n +CONFIG_ARM_KIRKWOOD_CPUFREQ=n +CONFIG_ARM_OMAP2PLUS_CPUFREQ=n +CONFIG_ARM_TI_CPUFREQ=y +CONFIG_QORIQ_CPUFREQ=n + +# AMx3 Power Config Options +CONFIG_WKUP_M3_RPROC=m +CONFIG_SOC_TI=y +CONFIG_WKUP_M3_IPC=m +CONFIG_AMX3_PM=m +CONFIG_SRAM=y +CONFIG_TI_EMIF_SRAM=m + +# Enable Reset Controllers +CONFIG_RESET_TI_SYSCON=y + +# Thermal +CONFIG_THERMAL_WRITABLE_TRIPS=y +CONFIG_THERMAL_GOV_FAIR_SHARE=y +CONFIG_THERMAL_GOV_BANG_BANG=y +CONFIG_THERMAL_GOV_USER_SPACE=y +CONFIG_TI_THERMAL=y +CONFIG_OMAP5_THERMAL=y +CONFIG_DRA752_THERMAL=y + +# Sensors +CONFIG_SENSORS_TMP102=y +CONFIG_SENSORS_GPIO_FAN=y + +# Enable the reset framework +CONFIG_POWER_RESET=y +CONFIG_POWER_SUPPLY=y + +# Pinctrl +CONFIG_PINCTRL_TI_IODELAY=y +CONFIG_PINCTRL_SINGLE=y + +# Multifunction device drivers +CONFIG_MFD_TPS65219=m +CONFIG_MFD_TPS6594X=y +CONFIG_TWL6040_CORE=y +CONFIG_MFD_TI_LP873X=y +CONFIG_MFD_TI_LP87565=y + +# Regulators +CONFIG_REGULATOR=y +CONFIG_REGULATOR_LP873X=y +CONFIG_REGULATOR_LP87565=y +CONFIG_REGULATOR_TPS65023=y +CONFIG_REGULATOR_TPS6507X=y +CONFIG_REGULATOR_TPS65219=m +CONFIG_REGULATOR_TPS6524X=y + +# Push Button +CONFIG_INPUT_TPS65219_PWRBUTTON=m + +# Crypto Modules +CONFIG_CRYPTO_DEV_OMAP=m +CONFIG_CRYPTO_DEV_OMAP_SHAM=m +CONFIG_CRYPTO_DEV_OMAP_AES=m +CONFIG_CRYPTO_DEV_OMAP_DES=m + +# RTC drivers +CONFIG_RTC_DRV_DS1307=m +CONFIG_RTC_DRV_PALMAS=m +CONFIG_RTC_DRV_TPS6586X=m +CONFIG_RTC_DRV_TPS65910=m +CONFIG_RTC_DRV_TPS6594X=y +CONFIG_RTC_DRV_TWL92330=y +CONFIG_RTC_DRV_OMAP=m + +# WatchDog +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_NOWAYOUT=n + +# Watchdog Device Drivers +CONFIG_OMAP_WATCHDOG=m +CONFIG_TWL4030_WATCHDOG=m + +# No Staging drivers please +CONFIG_STAGING=n + +# GPIO +CONFIG_GPIOLIB=y +CONFIG_GPIO_SYSFS=y +CONFIG_DEBUG_GPIO=y +CONFIG_GPIO_DAVINCI=y + +# Enable options to facilitate testing +CONFIG_CRYPTO_TEST=m +CONFIG_RTC_DEBUG=y +CONFIG_THERMAL_EMULATION=y + +# OPTEE Driver +CONFIG_TEE=y +CONFIG_OPTEE=y + +# DMA-BUF Heaps +CONFIG_DMABUF_HEAPS=y +CONFIG_DMABUF_HEAPS_SYSTEM=y +CONFIG_DMABUF_HEAPS_CMA=y +CONFIG_DMABUF_HEAPS_CARVEOUT=y +CONFIG_SRAM_DMA_HEAP=y + +# DMA-BUF exporter +CONFIG_DMA_BUF_PHYS=y +################################################## +# TI RPMsg/IPC Config Options +################################################## +# HwSpinLock +CONFIG_HWSPINLOCK=y +CONFIG_HWSPINLOCK_OMAP=y + +# Mailbox +CONFIG_MAILBOX=y +CONFIG_OMAP2PLUS_MBOX=y + +# SoC +CONFIG_TI_PRUSS=m + +# IrqChip Drivers +CONFIG_TI_PRUSS_INTC=m + +# IOMMU +CONFIG_IOMMU_SUPPORT=y +CONFIG_OMAP_IOMMU=y +CONFIG_OMAP_IOMMU_DEBUG=y + +# Remoteproc +CONFIG_OMAP_REMOTEPROC=m +CONFIG_OMAP_REMOTEPROC_WATCHDOG=y +CONFIG_PRU_REMOTEPROC=m +CONFIG_KEYSTONE_REMOTEPROC=m + +# RPMsg +CONFIG_RPMSG_CHAR=m +CONFIG_RPMSG_VIRTIO=m +CONFIG_RPMSG_PROTO=m +CONFIG_RPMSG_PRU=m + +# DSP Memory Mapper for Keystone MPM +CONFIG_KEYSTONE_DSP_MEM=m + +# UIO Module +CONFIG_UIO=m +################################################## +# TI Connectivity Configs +################################################## + +# Disable unused I2C options +CONFIG_I2C_MUX=n +CONFIG_I2C_DESIGNWARE_PLATFORM=n +CONFIG_I2C_GPIO=n +CONFIG_I2C_RK3X=n +CONFIG_I2C_XILINX=n + +# I2C controllers +CONFIG_I2C=y +CONFIG_I2C_OMAP=y + +# I2C GPIO expanders +CONFIG_GPIO_PCA953X=y +CONFIG_GPIO_PCA953X_IRQ=y +CONFIG_GPIO_PCF857X=y +CONFIG_GPIO_TPIC2810=m + +#I2C EEPROMS +CONFIG_EEPROM_AT24=m + +#SPI EEPROMS +CONFIG_EEPROM_93XX46=m + +# PTP +CONFIG_PTP_1588_CLOCK=y + +#Networking drivers +CONFIG_NET_VENDOR_TI=y +CONFIG_KEYSTONE_NAVIGATOR_QMSS=y +CONFIG_KEYSTONE_NAVIGATOR_DMA=y +CONFIG_TI_KEYSTONE_NETCP=y +CONFIG_TI_KEYSTONE_NETCP_ETHSS=y +CONFIG_TI_DAVINCI_EMAC=y +CONFIG_TI_DAVINCI_MDIO=y +CONFIG_MDIO_BITBANG=y +CONFIG_MDIO_GPIO=y +CONFIG_TI_CPSW=y +CONFIG_TI_CPSW_SWITCHDEV=y +CONFIG_TI_CPTS=y +CONFIG_TI_RDEV_ETH_SWITCH_VIRT_EMAC=m +CONFIG_TI_PRUETH=m +CONFIG_TI_ICSSG_PRUETH=m +CONFIG_TI_K3_AM65_CPSW_SWITCHDEV=y +# non-TI Net vendors +CONFIG_NET_DSA_BCM_SF2=n +CONFIG_B53=n +CONFIG_SYSTEMPORT=n +CONFIG_NET_VENDOR_3COM=n +CONFIG_NET_VENDOR_ADAPTEC=n +CONFIG_NET_VENDOR_AGERE=n +CONFIG_NET_VENDOR_ALTEON=n +CONFIG_NET_VENDOR_AMAZON=n +CONFIG_NET_VENDOR_AMD=n +CONFIG_NET_VENDOR_ARC=n +CONFIG_NET_VENDOR_ATHEROS=n +CONFIG_NET_VENDOR_BROCADE=n +CONFIG_NET_VENDOR_CAVIUM=n +CONFIG_NET_VENDOR_CHELSIO=n +CONFIG_BCMGENET=n +CONFIG_NET_VENDOR_CIRRUS=n +CONFIG_NET_VENDOR_CISCO=n +CONFIG_NET_VENDOR_DEC=n +CONFIG_NET_VENDOR_DLINK=n +CONFIG_NET_VENDOR_EMULEX=n +CONFIG_NET_VENDOR_EZCHIP=n +CONFIG_NET_VENDOR_FARADAY=n +CONFIG_NET_VENDOR_HISILICON=n +CONFIG_NET_VENDOR_HP=n +CONFIG_IGB=n +CONFIG_NET_VENDOR_I825XX=n +CONFIG_NET_VENDOR_MELLANOX=n +CONFIG_NET_VENDOR_MICROCHIP=n +CONFIG_NET_VENDOR_MYRI=n +CONFIG_NET_VENDOR_NATSEMI=n +CONFIG_NET_VENDOR_NETRONOME=n +CONFIG_NET_VENDOR_NVIDIA=n +CONFIG_NET_VENDOR_OKI=n +CONFIG_NET_VENDOR_8390=n +CONFIG_NET_VENDOR_QLOGIC=n +CONFIG_NET_VENDOR_REALTEK=n +CONFIG_NET_VENDOR_RENESAS=n +CONFIG_NET_VENDOR_RDC=n +CONFIG_NET_VENDOR_ROCKER=n +CONFIG_NET_VENDOR_SAMSUNG=n +CONFIG_NET_VENDOR_SILAN=n +CONFIG_NET_VENDOR_SIS=n +CONFIG_NET_VENDOR_SEEQ=n +CONFIG_NET_VENDOR_STMICRO=n +CONFIG_NET_VENDOR_SUN=n +CONFIG_NET_VENDOR_SYNOPSYS=n +CONFIG_NET_VENDOR_TEHUTI=n +CONFIG_NET_VENDOR_VIA=n +CONFIG_NET_VENDOR_WIZNET=n +#Wireless LAN +CONFIG_WLCORE=m +CONFIG_WLCORE_SDIO=m +CONFIG_WL18XX=m +CONFIG_NL80211_TESTMODE=y +CONFIG_MAC80211_MESH=y + +#MDIO phys +CONFIG_MARVELL_PHY=y +CONFIG_MICREL_PHY=y +# unused PHY drivers +CONFIG_BROADCOM_PHY=n +CONFIG_ICPLUS_PHY=n +#PRU MII PHYs for Industrial Boards +CONFIG_DP83848_PHY=y +# Enable phy for DRA72 evm +CONFIG_DP83867_PHY=y +# Enable phy for AM64 evm +CONFIG_DP83869_PHY=y +# Control onboard MDIO muxes +CONFIG_MDIO_BUS_MUX_MULTIPLEXER=y + +#MTD +CONFIG_MTD=y +CONFIG_MEMORY=y +CONFIG_TI_AEMIF=y +CONFIG_OMAP_GPMC=y +CONFIG_MTD_NAND_OMAP2=y +CONFIG_MTD_NAND_OMAP_BCH=y +CONFIG_MTD_NAND_OMAP_BCH_BUILD=y +CONFIG_MTD_NAND_DAVINCI=y +CONFIG_MTD_SPI_NAND=y +CONFIG_MTD_TESTS=m +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_UBI=y +CONFIG_MTD_HYPERBUS=y +CONFIG_HBMC_AM654=y + +#PCIE +CONFIG_SOCIONEXT_SYNQUACER_PREITS=y +CONFIG_PCI=y +CONFIG_PCI_MSI=y +CONFIG_PCI_ENDPOINT=y +CONFIG_PCI_ENDPOINT_CONFIGFS=y +CONFIG_PCI_EPF_TEST=y +CONFIG_PCI_EPF_NTB=y +CONFIG_PCI_ENDPOINT_TEST=m +CONFIG_PCI_DRA7XX=y +CONFIG_PCI_DRA7XX_HOST=y +CONFIG_PCI_DRA7XX_EP=y +CONFIG_PCI_KEYSTONE=y +CONFIG_PCI_KEYSTONE_HOST=y +CONFIG_PCI_KEYSTONE_EP=y +CONFIG_PCI_J721E=y +CONFIG_PCI_J721E_HOST=y +CONFIG_PCI_J721E_EP=y +CONFIG_PCIE_CADENCE=y +CONFIG_PCIE_CADENCE_HOST=y +CONFIG_PCIE_CADENCE_EP=y + +#NTB +CONFIG_NTB=m +CONFIG_NTB_EPF=m +CONFIG_NTB_TRANSPORT=m +CONFIG_NTB_NETDEV=m + +#NVME +CONFIG_NVME_CORE=m +CONFIG_BLK_DEV_NVME=m + +#These drivers have been used with DRA7x/AM57x PCIe RC with some success +CONFIG_NET_VENDOR_BROADCOM=y +CONFIG_NET_VENDOR_MARVELL=y +CONFIG_NET_VENDOR_INTEL=y +CONFIG_TIGON3=m +CONFIG_SKGE=m +CONFIG_E1000=m +CONFIG_E1000E=m +CONFIG_IWLWIFI=m +CONFIG_IWLDVM=m +CONFIG_IWLMVM=m +CONFIG_B43=m +#Generic Phys +CONFIG_PHY_AM654_SERDES=y +CONFIG_PHY_TI_KEYSTONE_SERDES=y +CONFIG_PHY_CADENCE_SIERRA=y + +# Networking +CONFIG_HSR=m +CONFIG_NF_CONNTRACK=m +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_ARPTABLES=m +CONFIG_IP_NF_ARPFILTER=m +CONFIG_IP_NF_ARP_MANGLE=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_BRIDGE=m +CONFIG_XFRM_USER=m +CONFIG_NET_KEY=m +CONFIG_INET=y +CONFIG_INET_AH=m +CONFIG_INET6_AH=m +CONFIG_INET6_IPCOMP=m +CONFIG_INET6_XFRM_TUNNEL=m +CONFIG_INET6_TUNNEL=m +CONFIG_INET_ESP=m +CONFIG_INET_IPCOMP=m +CONFIG_IPV6_TUNNEL=m +CONFIG_NETFILTER=y +CONFIG_NETFILTER_XTABLES=m +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m +CONFIG_NETFILTER_XT_TARGET_MARK=m +CONFIG_NETFILTER_XT_MATCH_COMMENT=m +CONFIG_NETFILTER_XT_MATCH_CPU=m +CONFIG_NETFILTER_XT_MATCH_IPRANGE=m +CONFIG_NETFILTER_XT_MATCH_LENGTH=m +CONFIG_NETFILTER_XT_MATCH_LIMIT=m +CONFIG_NETFILTER_XT_MATCH_MAC=m +CONFIG_NETFILTER_XT_MATCH_MARK=m +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m +CONFIG_NETFILTER_XT_MATCH_POLICY=m +CONFIG_NETFILTER_XT_MATCH_SCTP=m +CONFIG_BRIDGE_NF_EBTABLES=m +CONFIG_BRIDGE_EBT_BROUTE=m +CONFIG_BRIDGE_EBT_T_FILTER=m +CONFIG_BRIDGE_EBT_T_NAT=m +CONFIG_BRIDGE_EBT_802_3=m +CONFIG_BRIDGE_EBT_AMONG=m +CONFIG_BRIDGE_EBT_ARP=m +CONFIG_BRIDGE_EBT_IP=m +CONFIG_BRIDGE_EBT_IP6=m +CONFIG_BRIDGE_EBT_LIMIT=m +CONFIG_BRIDGE_EBT_MARK=m +CONFIG_BRIDGE_EBT_PKTTYPE=m +CONFIG_BRIDGE_EBT_STP=m +CONFIG_BRIDGE_EBT_VLAN=m +CONFIG_BRIDGE_EBT_ARPREPLY=m +CONFIG_BRIDGE_EBT_DNAT=m +CONFIG_BRIDGE_EBT_MARK_T=m +CONFIG_BRIDGE_EBT_REDIRECT=m +CONFIG_BRIDGE_EBT_SNAT=m +CONFIG_BRIDGE_EBT_LOG=m +CONFIG_BRIDGE_EBT_NFLOG=m +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CBQ=m +CONFIG_NET_SCH_HTB=m +CONFIG_NET_SCH_HFSC=m +CONFIG_NET_SCH_PRIO=m +CONFIG_NET_SCH_MULTIQ=m +CONFIG_NET_SCH_RED=m +CONFIG_NET_SCH_SFB=m +CONFIG_NET_SCH_SFQ=m +CONFIG_NET_SCH_TEQL=m +CONFIG_NET_SCH_TBF=m +CONFIG_NET_SCH_GRED=m +CONFIG_NET_SCH_DSMARK=m +CONFIG_NET_SCH_NETEM=m +CONFIG_NET_SCH_DRR=m +CONFIG_NET_SCH_MQPRIO=m +CONFIG_NET_SCH_TAPRIO=m +CONFIG_NET_SCH_CHOKE=m +CONFIG_NET_SCH_QFQ=m +CONFIG_NET_SCH_CODEL=m +CONFIG_NET_SCH_FQ_CODEL=m +CONFIG_NET_SCH_INGRESS=m +CONFIG_NET_CLS=y +CONFIG_NET_CLS_BASIC=m +CONFIG_NET_CLS_TCINDEX=m +CONFIG_NET_CLS_ROUTE4=m +CONFIG_NET_CLS_FW=m +CONFIG_NET_CLS_U32=m +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_RSVP=m +CONFIG_NET_CLS_RSVP6=m +CONFIG_NET_CLS_FLOW=m +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +CONFIG_NET_EMATCH_CMP=m +CONFIG_NET_EMATCH_NBYTE=m +CONFIG_NET_EMATCH_U32=m +CONFIG_NET_EMATCH_META=m +CONFIG_NET_EMATCH_TEXT=m +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=m +CONFIG_NET_ACT_GACT=m +CONFIG_GACT_PROB=y +CONFIG_NET_ACT_MIRRED=m +CONFIG_NET_ACT_IPT=m +CONFIG_NET_ACT_NAT=m +CONFIG_NET_ACT_PEDIT=m +CONFIG_NET_ACT_SIMP=m +CONFIG_NET_ACT_SKBEDIT=m +CONFIG_NET_ACT_CSUM=m +CONFIG_NET_SCH_FIFO=y +CONFIG_IP_SCTP=m +CONFIG_VLAN_8021Q=m +CONFIG_IP_MULTICAST=y +CONFIG_NET_SWITCHDEV=y + +# Ethernet drivers +CONFIG_TI_K3_AM65_CPSW_NUSS=y +CONFIG_TI_AM65_CPSW_TAS=y +CONFIG_TI_K3_AM65_CPTS=y +#MMC/SD support +CONFIG_MMC=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_OMAP_HS=y +CONFIG_MMC_SDHCI_OMAP=y +CONFIG_MMC_DW=n +CONFIG_MMC_SDHCI_AM654=y + +#SPI +CONFIG_SPI_CADENCE_QUADSPI=y +CONFIG_SPI_TI_QSPI=y +CONFIG_SPI_OMAP24XX=y +CONFIG_SPI_SLAVE=y +CONFIG_SPI_SPIDEV=m +#Disable unused SPI controllers +CONFIG_SPI_BITBANG=n +CONFIG_SPI_CADENCE=n +CONFIG_SPI_ROCKCHIP=n +CONFIG_SPI_XILINX=n + +#Disable SPI NOR 4K SECTORS +CONFIG_MTD_SPI_NOR_USE_4K_SECTORS=n + +#SPI GPIO expanders +CONFIG_GPIO_PISOSR=m + +#SATA +CONFIG_SATA_AHCI_PLATFORM=m +CONFIG_SATA_AHCI=m +CONFIG_ATA=m + +#USB PHY +CONFIG_OMAP_USB2=m + +#USB gadgets +CONFIG_USB_GADGET=m +CONFIG_USB_AUDIO=m +CONFIG_USB_ETH=m +CONFIG_USB_G_NCM=m +CONFIG_USB_GADGETFS=m +CONFIG_USB_FUNCTIONFS=m +CONFIG_USB_FUNCTIONFS_ETH=y +CONFIG_USB_FUNCTIONFS_RNDIS=y +CONFIG_USB_FUNCTIONFS_GENERIC=y +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_G_SERIAL=m +CONFIG_USB_MIDI_GADGET=m +CONFIG_USB_G_PRINTER=m +CONFIG_USB_CDC_COMPOSITE=m +CONFIG_USB_G_ACM_MS=m +CONFIG_USB_G_MULTI=m +CONFIG_USB_G_MULTI_CDC=y +CONFIG_USB_G_HID=m +CONFIG_USB_G_DBGP=m +CONFIG_USB_G_WEBCAM=m +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=32 +CONFIG_USB_ZERO=m +CONFIG_USB_CONFIGFS=m +CONFIG_USB_CONFIGFS_SERIAL=y +CONFIG_USB_CONFIGFS_ACM=y +CONFIG_USB_CONFIGFS_OBEX=y +CONFIG_USB_CONFIGFS_NCM=y +CONFIG_USB_CONFIGFS_ECM=y +CONFIG_USB_CONFIGFS_ECM_SUBSET=y +CONFIG_USB_CONFIGFS_RNDIS=y +CONFIG_USB_CONFIGFS_EEM=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +CONFIG_USB_CONFIGFS_F_LB_SS=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_UAC1=y +CONFIG_USB_CONFIGFS_F_UAC2=y +CONFIG_USB_CONFIGFS_F_MIDI=y +CONFIG_USB_CONFIGFS_F_HID=y +CONFIG_USB_CONFIGFS_F_UVC=y +CONFIG_USB_CONFIGFS_F_PRINTER=y + +# USB DWC3 +CONFIG_USB_DWC3=m +CONFIG_USB_DWC3_DUAL_ROLE=y +CONFIG_USB_DWC3_OMAP=m +CONFIG_USB_DWC3_KEYSTONE=m +CONFIG_USB_DWC3_AM62=m +CONFIG_USB_DWC3_PCI=n +CONFIG_USB_DWC2=n +CONFIG_USB_CHIPIDEA=n + +# USB CDNS3 +CONFIG_USB_CDNS3=m +CONFIG_USB_CDNS3_GADGET=y +CONFIG_USB_CDNS3_HOST=y +CONFIG_USB_CDNS3_TI=m + +#USB PHY +CONFIG_NOP_USB_XCEIV=m + +#USB MUSB +CONFIG_USB_MUSB_HDRC=m +CONFIG_USB_MUSB_DUAL_ROLE=y +CONFIG_USB_MUSB_OMAP2PLUS=m +CONFIG_USB_MUSB_AM35X=m +CONFIG_USB_MUSB_DSPS=m +CONFIG_USB_MUSB_AM335X_CHILD=m +CONFIG_TI_CPPI41=y +CONFIG_USB_TI_CPPI41_DMA=y +CONFIG_AM335X_CONTROL_USB=y +CONFIG_AM335X_PHY_USB=y + +#USB EHCI +CONFIG_USB=m +CONFIG_USB_EHCI_HCD=m +CONFIG_USB_EHCI_PCI=m +CONFIG_USB_EHCI_HCD_OMAP=m + +#USB Networking +CONFIG_USB_NET_DRIVERS=m +CONFIG_USB_PEGASUS=m +CONFIG_USB_RTL8150=m +CONFIG_USB_RTL8152=m +CONFIG_USB_LAN78XX=m +CONFIG_USB_USBNET=m +CONFIG_USB_NET_AX8817X=m +CONFIG_USB_NET_AX88179_178A=m +CONFIG_USB_NET_CDCETHER=m +CONFIG_USB_NET_CDC_EEM=m +CONFIG_USB_NET_CDC_NCM=m +CONFIG_USB_NET_SMSC75XX=m +CONFIG_USB_NET_SMSC95XX=m +CONFIG_USB_NET_NET1080=m +CONFIG_USB_NET_CDC_SUBSET_ENABLE=m +CONFIG_USB_NET_CDC_SUBSET=m +CONFIG_USB_BELKIN=y +CONFIG_USB_ARMLINUX=y +CONFIG_USB_NET_ZAURUS=m + +#USB testing +CONFIG_USB_TEST=m +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +#USB Serial +CONFIG_USB_ACM=m +CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL_CP210X=m +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_PL2303=m +CONFIG_USB_SERIAL_OPTION=m + +# Extcon drivers +CONFIG_EXTCON=m +CONFIG_EXTCON_PALMAS=m +CONFIG_EXTCON_USB_GPIO=m + +# USB PD controller drivers +CONFIG_TYPEC_TPS6598X=m + +# PWM +CONFIG_PWM_TIECAP=y +CONFIG_PWM_TIEHRPWM=y +CONFIG_PWM_TIPWMSS=y +CONFIG_PWM_OMAP_DMTIMER=y + +# eCAP driver +CONFIG_TI_ECAP_CAPTURE=m + +# 1-wire Bus Masters +CONFIG_W1=m +CONFIG_HDQ_MASTER_OMAP=m + +# Matrix keypad +CONFIG_KEYBOARD_MATRIX=m + +#Touchscreen/ADC +CONFIG_MFD_TI_AM335X_TSCADC=m +CONFIG_TOUCHSCREEN_TI_AM335X_TSC=m +CONFIG_TI_AM335X_ADC=m + +#CAN +CONFIG_CAN=m +CONFIG_CAN_C_CAN=m +CONFIG_CAN_C_CAN_PLATFORM=m +CONFIG_CAN_M_CAN=m +CONFIG_CAN_M_CAN_PLATFORM=m + +# CAN Transceiver +CONFIG_PHY_CAN_TRANSCEIVER=m + +# Rotary Encoder +CONFIG_INPUT_GPIO_DECODER=m + +# Filesystem extra options +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_CIFS=m +CONFIG_CIFS_STATS=y +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +CONFIG_UBIFS_FS=y + +# HD-Audio +CONFIG_SND_USB=y +CONFIG_SND_USB_AUDIO=m + +#UFS +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_SCSI_UFSHCD=y +CONFIG_SCSI_UFS_BSG=y +CONFIG_SCSI_UFSHCD_PLATFORM=y +CONFIG_SCSI_UFS_CDNS_PLATFORM=y +CONFIG_SCSI_UFS_TI_J721E=y + +#PRUSS-UART +CONFIG_SERIAL_8250_PRUSS=m + +# MUX Drivers +CONFIG_MUX_GPIO=y + +# PRU Soft UART driver +CONFIG_SERIAL_PRU_SWUART=m + +# Enable Power line communication Common drivers +CONFIG_NET_VENDOR_QUALCOMM=y +CONFIG_QCA7000=m +CONFIG_QCA7000_SPI=m +CONFIG_QCA7000_UART=m + +# AF_XDP +CONFIG_BPF_SYSCALL=y +CONFIG_XDP_SOCKETS=y +################################################## +# TI Audio/Display config options +################################################## + +CONFIG_CMA=y +CONFIG_DMA_CMA=y +CONFIG_CMA_SIZE_MBYTES=512 + +# backlight + +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_TLC591XX=y + +CONFIG_BACKLIGHT_PWM=y +CONFIG_BACKLIGHT_GPIO=y +CONFIG_BACKLIGHT_LED=y + +# fbdev + +CONFIG_FB_SSD1307=y +CONFIG_FRAMEBUFFER_CONSOLE=y + +# drm + +CONFIG_DRM=y +CONFIG_DRM_DISPLAY_CONNECTOR=y +CONFIG_DRM_SII902X=y +CONFIG_DRM_TI_TFP410=y +CONFIG_DRM_TI_TPD12S015=y +CONFIG_DRM_LVDS_CODEC=y +CONFIG_DRM_TOSHIBA_TC358767=y +CONFIG_DRM_TOSHIBA_TC358768=y +CONFIG_DRM_PANEL_SIMPLE=y +CONFIG_DRM_PANEL_OSD_OSD101T2587_53TS=y +# Firmware loading only works if built as module +CONFIG_DRM_CDNS_MHDP8546=m +CONFIG_DRM_CDNS_DSI=m + +CONFIG_PHY_J721E_WIZ=y +CONFIG_PHY_CADENCE_TORRENT=y + +# SGX driver needs legacy support +CONFIG_DRM_LEGACY=y +CONFIG_DRM_VM=y + +# tilcdc + +CONFIG_DRM_I2C_NXP_TDA998X=y +CONFIG_DRM_TILCDC=y + +# omapdrm + +CONFIG_DRM_OMAP=y +CONFIG_DRM_OMAP_WB=y +CONFIG_OMAP2_DSS=y +CONFIG_OMAP2_DSS_DEBUGFS=y +CONFIG_OMAP2_DSS_DPI=y +CONFIG_OMAP2_DSS_VENC=n +CONFIG_OMAP4_DSS_HDMI=y +CONFIG_OMAP5_DSS_HDMI=y +CONFIG_OMAP2_DSS_SDI=n +CONFIG_OMAP2_DSS_DSI=n + +# tidss +CONFIG_DRM_TIDSS=y +CONFIG_DRM_TIDSS_WB=y +CONFIG_DRM_TIDSS_DSS6=y +CONFIG_DRM_TIDSS_DSS7=y + +# Touchscreen + +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_EDT_FT5X06=m +CONFIG_TOUCHSCREEN_PIXCIR=m +CONFIG_TOUCHSCREEN_GOODIX=m +CONFIG_TOUCHSCREEN_ILI210X=m + +CONFIG_HID_MULTITOUCH=m + +# V4L2 + +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y + +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_VIDEO_AM437X_VPFE=m +CONFIG_VIDEO_TI_CAL=m +CONFIG_VIDEO_TI_VIP=m + +CONFIG_VIDEO_CADENCE=y +CONFIG_VIDEO_CADENCE_CSI2RX=m +CONFIG_PHY_CADENCE_DPHY=m +CONFIG_VIDEO_TI_J721E_CSI2RX=m + +CONFIG_V4L_MEM2MEM_DRIVERS=y +CONFIG_VIDEO_TI_VPE=m + +CONFIG_MEDIA_SUBDRV_AUTOSELECT=n +CONFIG_VIDEO_OV2659=m +CONFIG_VIDEO_OV1063X=m +CONFIG_VIDEO_MT9T11X=m +CONFIG_VIDEO_OV490=m +CONFIG_VIDEO_OV5640=m +CONFIG_VIDEO_IMX390=m + +CONFIG_VIDEO_DS90UB960=m +CONFIG_VIDEO_DS90UB953=m + +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_USB_VIDEO_CLASS=m +CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y + +CONFIG_MEDIA_ANALOG_TV_SUPPORT=n +CONFIG_MEDIA_DIGITAL_TV_SUPPORT=n +CONFIG_RC_CORE=n + +# sound +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_SIMPLE_CARD=m +CONFIG_SND_SIMPLE_SCU_CARD=m +CONFIG_SND_AUDIO_GRAPH_CARD=m +CONFIG_SND_AUDIO_GRAPH_SCU_CARD=m +CONFIG_SND_SOC_DAVINCI_MCASP=y +CONFIG_SND_SOC_OMAP_HDMI=m +CONFIG_SND_SOC_J721E_EVM=m +CONFIG_SND_SOC_TLV320AIC3X=m +CONFIG_SND_SOC_TLV320AIC31XX=m +CONFIG_SND_SOC_PCM3168A_I2C=m +CONFIG_SND_SOC_HDMI_CODEC=m + +# display sharing +CONFIG_RPMSG_KDRV_DISPLAY=y +################################################## +# TI Base OMAP generation SoCs generic default +################################################## + +CONFIG_ARCH_MULTI_V7=y +CONFIG_ARCH_MULTI_V6_V7=y +CONFIG_ARCH_OMAP=y +CONFIG_SOC_OMAP5=y +CONFIG_SOC_AM43XX=y +CONFIG_SOC_DRA7XX=y +CONFIG_ARCH_OMAP2PLUS=y +CONFIG_OMAP_INTERCONNECT_BARRIER=y +CONFIG_ARM_PSCI=n + +# Maximum Number of processors +CONFIG_NR_CPUS=2 + +# Kexec +CONFIG_KEXEC=y +################################################## +# TI AM33XX specific config options +################################################## + +# Enable AM33xx +CONFIG_SOC_AM33XX=y + +#Disable CONFIG_SMP +CONFIG_SMP=n +CONFIG_CPUSETS=n +############################################## +# +# Kernel options needed for systemd enabled TI SDKs +# See https://cgit.freedesktop.org/systemd/systemd/tree/README#n38 for details +# +############################################## +CONFIG_TMPFS=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_BLK_CGROUP=y +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_PERF=y + +CONFIG_INOTIFY_USER=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EPOLL=y +CONFIG_NET=y +CONFIG_SYSFS=y +CONFIG_PROC_FS=y +CONFIG_FHANDLE=y + +# udev will fail to work with the legacy sysfs layout: +CONFIG_SYSFS_DEPRECATED=n + +# Legacy hotplug slows down the system and confuses udev: +CONFIG_UEVENT_HELPER_PATH="" + +# Userspace firmware loading is not supported and should +# be disabled in the kernel: +CONFIG_FW_LOADER_USER_HELPER=n + +# Some udev rules and virtualization detection relies on it: +# Only for UEFI based systems +# http://cateee.net/lkddb/web-lkddb/DMI.html +CONFIG_DMIID=n + +# Support for some SCSI devices serial number retrieval, to +# create additional symlinks in /dev/disk/ and /dev/tape: +CONFIG_BLK_DEV_BSG=y + +# Required for PrivateNetwork and PrivateDevices in service units: +# Note that systemd-localed.service and other systemd units use +# PrivateNetwork and PrivateDevices so this is effectively required. +CONFIG_NAMESPACES=y +CONFIG_NET_NS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y + +# Optional but strongly recommended options: those are nice to have and +# indeed recommended, but not necessarily systemd required. These to be +# enabled in corresponding domain fragments since they are not specific +# to supporting systemd. + +CONFIG_SECCOMP=y +# for kcmp syscall +CONFIG_CHECKPOINT_RESTORE=y + +# Required for CPUShares= in resource control unit settings +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y + +# Required for CPUQuota= in resource control unit settings +CONFIG_CFS_BANDWIDTH=y + +# For systemd-bootchart, several proc debug interfaces are required: +# Systemd-debug.cfg? +CONFIG_SCHEDSTATS=y +CONFIG_SCHED_DEBUG=y + +# We recommend to turn off Real-Time group scheduling in the +# kernel when using systemd. RT group scheduling effectively +# makes RT scheduling unavailable for most userspace, since it +# requires explicit assignment of RT budgets to each unit whose +# processes making use of RT. As there's no sensible way to +# assign these budgets automatically this cannot really be +# fixed, and it's best to disable group scheduling hence. +CONFIG_RT_GROUP_SCHED=n + +# Note that kernel auditing is broken when used with systemd's +# container code. When using systemd in conjunction with +# containers, please make sure to either turn off auditing at +# runtime using the kernel command line option "audit=0", or +# turn it off at kernel compile time using: +CONFIG_AUDIT=n +############################################## +# +# Kernel options needed for container enabled TI SDKs +# See https://raw.githubusercontent.com/moby/moby/v20.10.12/contrib/check-config.sh +# Also see lxc-checkconfig +# +############################################## + +# Just the basic container configuration necessary +CONFIG_CGROUP_BPF=y +CONFIG_VETH=m +CONFIG_BRIDGE_NETFILTER=m +CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m +CONFIG_NETFILTER_XT_MATCH_IPVS=m +CONFIG_NET_CLS_CGROUP=m +CONFIG_IP_VS=m +CONFIG_IP_VS_RR=m +CONFIG_VXLAN=m +CONFIG_IPVLAN=m +CONFIG_MACVLAN=m +CONFIG_DUMMY=m +CONFIG_NF_NAT_FTP=m +CONFIG_NF_CONNTRACK_FTP=m +CONFIG_NF_NAT_TFTP=m +CONFIG_NF_CONNTRACK_TFTP=m +CONFIG_AUFS_FS=m +CONFIG_BTRFS_FS=m +CONFIG_BLK_DEV_DM=m +CONFIG_DM_THIN_PROVISIONING=m +CONFIG_OVERLAY_FS=m +CONFIG_INET_XFRM_MODE_TRANSPORT=m diff --git a/arch/arm/configs/ti_sdk_dra7x_release_defconfig b/arch/arm/configs/ti_sdk_dra7x_release_defconfig new file mode 100644 index 0000000000000..d3fa67e3c0c11 --- /dev/null +++ b/arch/arm/configs/ti_sdk_dra7x_release_defconfig @@ -0,0 +1,2094 @@ +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_PERF_EVENTS=y +CONFIG_MACH_ARTPEC6=y +CONFIG_MACH_ASPEED_G6=y +CONFIG_SOC_SAMA5D2=y +CONFIG_SOC_SAMA5D3=y +CONFIG_SOC_SAMA5D4=y +CONFIG_ARCH_BCM_HR2=y +CONFIG_ARCH_BCM2835=y +CONFIG_ARCH_BCM_63XX=y +CONFIG_MACH_BERLIN_BG2=y +CONFIG_MACH_BERLIN_BG2CD=y +CONFIG_MACH_BERLIN_BG2Q=y +CONFIG_SOC_IMX50=y +CONFIG_SOC_IMX51=y +CONFIG_SOC_IMX53=y +CONFIG_SOC_IMX6Q=y +CONFIG_SOC_IMX6SL=y +CONFIG_SOC_IMX6SLL=y +CONFIG_SOC_IMX6SX=y +CONFIG_SOC_IMX6UL=y +CONFIG_SOC_LS1021A=y +CONFIG_SOC_IMX7D=y +CONFIG_SOC_IMX7ULP=y +CONFIG_SOC_VF610=y +CONFIG_ARCH_MILBEAUT_M10V=y +CONFIG_MACH_MMP2_DT=y +CONFIG_MACH_MMP3_DT=y +CONFIG_MACH_ARMADA_370=y +CONFIG_MACH_ARMADA_375=y +CONFIG_MACH_ARMADA_38X=y +CONFIG_MACH_ARMADA_39X=y +CONFIG_MACH_ARMADA_XP=y +CONFIG_MACH_DOVE=y +CONFIG_MACH_SPEAR1310=y +CONFIG_MACH_SPEAR1340=y +CONFIG_ARM_APPENDED_DTB=y +CONFIG_ARM_ATAG_DTB_COMPAT=y +CONFIG_EFI=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y +CONFIG_ARM_IMX6Q_CPUFREQ=y +CONFIG_ARM_RASPBERRYPI_CPUFREQ=y +CONFIG_ARM_ZYNQ_CPUIDLE=y +CONFIG_ARM_EXYNOS_CPUIDLE=y +CONFIG_ARM_TEGRA_CPUIDLE=y +CONFIG_KERNEL_MODE_NEON=y +CONFIG_RASPBERRYPI_FIRMWARE=y +CONFIG_TRUSTED_FOUNDATIONS=y +CONFIG_BCM47XX_NVRAM=y +CONFIG_BCM47XX_SPROM=y +CONFIG_EFI_VARS=m +CONFIG_EFI_CAPSULE_LOADER=m +CONFIG_ARM_CRYPTO=y +CONFIG_CRYPTO_SHA1_ARM_NEON=m +CONFIG_CRYPTO_SHA1_ARM_CE=m +CONFIG_CRYPTO_SHA2_ARM_CE=m +CONFIG_CRYPTO_SHA512_ARM=m +CONFIG_CRYPTO_AES_ARM=m +CONFIG_CRYPTO_AES_ARM_BS=m +CONFIG_CRYPTO_AES_ARM_CE=m +CONFIG_CRYPTO_GHASH_ARM_CE=m +CONFIG_CRYPTO_CRC32_ARM_CE=m +CONFIG_CRYPTO_CHACHA20_NEON=m +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_CMDLINE_PARTITION=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_ESP=m +CONFIG_IPV6_MIP6=m +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_NET_DSA=m +CONFIG_CAN_AT91=m +CONFIG_CAN_FLEXCAN=m +CONFIG_CAN_SUN4I=y +CONFIG_CAN_XILINXCAN=y +CONFIG_CAN_RCAR=m +CONFIG_CAN_MCP251X=y +CONFIG_BT=m +CONFIG_BT_HCIUART=m +CONFIG_BT_HCIUART_BCM=y +CONFIG_BT_MRVL=m +CONFIG_BT_MRVL_SDIO=m +CONFIG_CFG80211=m +CONFIG_MAC80211=m +CONFIG_RFKILL=y +CONFIG_RFKILL_INPUT=y +CONFIG_RFKILL_GPIO=y +CONFIG_NFC=m +CONFIG_NFC_DIGITAL=m +CONFIG_NFC_NCI=m +CONFIG_NFC_NCI_SPI=m +CONFIG_NFC_NCI_UART=m +CONFIG_NFC_HCI=m +CONFIG_NFC_SHDLC=y +CONFIG_NFC_S3FWRN5_I2C=m +CONFIG_PCIEPORTBUS=y +CONFIG_PCI_MVEBU=y +CONFIG_PCI_TEGRA=y +CONFIG_PCI_RCAR_GEN2=y +CONFIG_PCIE_RCAR_HOST=y +CONFIG_OMAP_OCP2SCP=y +CONFIG_SIMPLE_PM_BUS=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_CFI=y +CONFIG_MTD_CFI_INTELEXT=y +CONFIG_MTD_PHYSMAP=y +CONFIG_MTD_PHYSMAP_OF=y +CONFIG_MTD_RAW_NAND=y +CONFIG_MTD_NAND_DENALI_DT=y +CONFIG_MTD_NAND_ATMEL=y +CONFIG_MTD_NAND_MARVELL=y +CONFIG_MTD_NAND_BRCMNAND=y +CONFIG_MTD_NAND_GPMI_NAND=y +CONFIG_MTD_NAND_VF610_NFC=y +CONFIG_MTD_NAND_STM32_FMC2=y +CONFIG_MTD_SPI_NOR=y +CONFIG_SPI_ASPEED_SMC=m +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=65536 +CONFIG_VIRTIO_BLK=y +CONFIG_AD525X_DPOT=y +CONFIG_AD525X_DPOT_I2C=y +CONFIG_ATMEL_TCLIB=y +CONFIG_ICS932S401=y +CONFIG_ATMEL_SSC=m +CONFIG_QCOM_COINCELL=m +CONFIG_APDS9802ALS=y +CONFIG_ISL29003=y +CONFIG_BLK_DEV_SR=y +CONFIG_AHCI_BRCM=y +CONFIG_AHCI_DM816=y +CONFIG_AHCI_ST=y +CONFIG_AHCI_IMX=y +CONFIG_AHCI_SUNXI=y +CONFIG_AHCI_TEGRA=y +CONFIG_SATA_HIGHBANK=y +CONFIG_SATA_MV=y +CONFIG_SATA_RCAR=y +CONFIG_NETDEVICES=y +CONFIG_VIRTIO_NET=y +CONFIG_B53_SPI_DRIVER=m +CONFIG_B53_MDIO_DRIVER=m +CONFIG_B53_MMAP_DRIVER=m +CONFIG_SUN4I_EMAC=y +CONFIG_BGMAC_BCMA=y +CONFIG_MACB=y +CONFIG_NET_CALXEDA_XGMAC=y +CONFIG_FTGMAC100=m +CONFIG_GIANFAR=y +CONFIG_HIX5HD2_GMAC=y +CONFIG_MV643XX_ETH=y +CONFIG_MVNETA=y +CONFIG_PXA168_ETH=m +CONFIG_KS8851=y +CONFIG_R8169=y +CONFIG_SH_ETH=y +CONFIG_SMSC911X=y +CONFIG_SNI_AVE=y +CONFIG_STMMAC_ETH=y +CONFIG_DWMAC_DWC_QOS_ETH=y +CONFIG_XILINX_EMACLITE=y +CONFIG_AT803X_PHY=y +CONFIG_ROCKCHIP_PHY=y +CONFIG_SMSC_PHY=y +CONFIG_BRCMFMAC=m +CONFIG_MWIFIEX=m +CONFIG_MWIFIEX_SDIO=m +CONFIG_RT2X00=m +CONFIG_RT2800USB=m +CONFIG_INPUT_JOYDEV=y +CONFIG_INPUT_EVDEV=y +CONFIG_KEYBOARD_QT1070=m +CONFIG_KEYBOARD_GPIO=y +CONFIG_KEYBOARD_TEGRA=y +CONFIG_KEYBOARD_PXA27x=m +CONFIG_KEYBOARD_SAMSUNG=m +CONFIG_KEYBOARD_ST_KEYSCAN=y +CONFIG_KEYBOARD_SPEAR=y +CONFIG_KEYBOARD_CROS_EC=m +CONFIG_MOUSE_PS2_ELANTECH=y +CONFIG_MOUSE_CYAPA=m +CONFIG_MOUSE_ELAN_I2C=y +CONFIG_TOUCHSCREEN_ADC=m +CONFIG_TOUCHSCREEN_ATMEL_MXT=m +CONFIG_TOUCHSCREEN_ELAN=m +CONFIG_TOUCHSCREEN_MMS114=m +CONFIG_TOUCHSCREEN_WM97XX=m +CONFIG_TOUCHSCREEN_ST1232=m +CONFIG_TOUCHSCREEN_STMPE=y +CONFIG_TOUCHSCREEN_SUN4I=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_MAX77693_HAPTIC=m +CONFIG_INPUT_MAX8997_HAPTIC=m +CONFIG_INPUT_CPCAP_PWRBUTTON=m +CONFIG_INPUT_AXP20X_PEK=m +CONFIG_INPUT_DA9063_ONKEY=m +CONFIG_INPUT_ADXL34X=m +CONFIG_INPUT_STPMIC1_ONKEY=y +CONFIG_SERIO_AMBAKMI=y +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_ASPEED_VUART=m +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_8250_BCM2835AUX=y +CONFIG_SERIAL_8250_MT6577=y +CONFIG_SERIAL_8250_UNIPHIER=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_SERIAL_ATMEL=y +CONFIG_SERIAL_ATMEL_CONSOLE=y +CONFIG_SERIAL_ATMEL_TTYAT=y +CONFIG_SERIAL_MESON=y +CONFIG_SERIAL_MESON_CONSOLE=y +CONFIG_SERIAL_SAMSUNG=y +CONFIG_SERIAL_SAMSUNG_CONSOLE=y +CONFIG_SERIAL_SIRFSOC=y +CONFIG_SERIAL_SIRFSOC_CONSOLE=y +CONFIG_SERIAL_TEGRA=y +CONFIG_SERIAL_IMX=y +CONFIG_SERIAL_IMX_CONSOLE=y +CONFIG_SERIAL_SH_SCI=y +CONFIG_SERIAL_SH_SCI_NR_UARTS=20 +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +CONFIG_SERIAL_VT8500=y +CONFIG_SERIAL_VT8500_CONSOLE=y +CONFIG_SERIAL_OMAP_CONSOLE=y +CONFIG_SERIAL_BCM63XX=y +CONFIG_SERIAL_BCM63XX_CONSOLE=y +CONFIG_SERIAL_XILINX_PS_UART_CONSOLE=y +CONFIG_SERIAL_FSL_LPUART_CONSOLE=y +CONFIG_SERIAL_CONEXANT_DIGICOLOR_CONSOLE=y +CONFIG_SERIAL_ST_ASC_CONSOLE=y +CONFIG_SERIAL_STM32=y +CONFIG_SERIAL_STM32_CONSOLE=y +CONFIG_SERIAL_DEV_BUS=y +CONFIG_VIRTIO_CONSOLE=y +CONFIG_ASPEED_KCS_IPMI_BMC=m +CONFIG_ASPEED_BT_IPMI_BMC=m +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_ST=y +CONFIG_TCG_TPM=m +CONFIG_TCG_TIS_I2C_INFINEON=m +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_ARB_GPIO_CHALLENGE=m +CONFIG_I2C_MUX_PCA954x=y +CONFIG_I2C_MUX_PINCTRL=y +CONFIG_I2C_DEMUX_PINCTRL=y +CONFIG_I2C_ASPEED=m +CONFIG_I2C_AT91=m +CONFIG_I2C_BCM2835=y +CONFIG_I2C_CADENCE=y +CONFIG_I2C_DAVINCI=y +CONFIG_I2C_DIGICOLOR=m +CONFIG_I2C_EMEV2=m +CONFIG_I2C_IMX=y +CONFIG_I2C_MESON=y +CONFIG_I2C_MV64XXX=y +CONFIG_I2C_RIIC=y +CONFIG_I2C_S3C2410=y +CONFIG_I2C_SH_MOBILE=y +CONFIG_I2C_SIRF=y +CONFIG_I2C_ST=y +CONFIG_I2C_STM32F7=y +CONFIG_I2C_SUN6I_P2WI=y +CONFIG_I2C_TEGRA=y +CONFIG_I2C_UNIPHIER=y +CONFIG_I2C_UNIPHIER_F=y +CONFIG_I2C_RCAR=y +CONFIG_I2C_CROS_EC_TUNNEL=m +CONFIG_I2C_SLAVE_EEPROM=y +CONFIG_SPI=y +CONFIG_SPI_ATMEL=m +CONFIG_SPI_BCM2835=y +CONFIG_SPI_BCM2835AUX=y +CONFIG_SPI_DAVINCI=y +CONFIG_SPI_FSL_QUADSPI=m +CONFIG_SPI_GPIO=m +CONFIG_SPI_FSL_DSPI=m +CONFIG_SPI_ORION=y +CONFIG_SPI_PL022=y +CONFIG_SPI_RSPI=y +CONFIG_SPI_S3C64XX=m +CONFIG_SPI_SH_MSIOF=m +CONFIG_SPI_SH_HSPI=y +CONFIG_SPI_SIRF=y +CONFIG_SPI_STM32=m +CONFIG_SPI_STM32_QSPI=y +CONFIG_SPI_SUN4I=y +CONFIG_SPI_SUN6I=y +CONFIG_SPI_TEGRA114=y +CONFIG_SPI_TEGRA20_SFLASH=y +CONFIG_SPI_TEGRA20_SLINK=y +CONFIG_SPMI=y +CONFIG_PINCTRL_AS3722=y +CONFIG_PINCTRL_RZA2=y +CONFIG_PINCTRL_STMFX=y +CONFIG_PINCTRL_PALMAS=y +CONFIG_PINCTRL_APQ8064=y +CONFIG_PINCTRL_APQ8084=y +CONFIG_PINCTRL_IPQ8064=y +CONFIG_PINCTRL_MSM8660=y +CONFIG_PINCTRL_MSM8960=y +CONFIG_PINCTRL_MSM8X74=y +CONFIG_PINCTRL_MSM8916=y +CONFIG_PINCTRL_QCOM_SPMI_PMIC=y +CONFIG_PINCTRL_QCOM_SSBI_PMIC=y +CONFIG_GPIO_ASPEED_SGPIO=y +CONFIG_GPIO_EM=y +CONFIG_GPIO_RCAR=y +CONFIG_GPIO_SYSCON=y +CONFIG_GPIO_UNIPHIER=y +CONFIG_GPIO_ZYNQ=y +CONFIG_GPIO_PALMAS=y +CONFIG_GPIO_TPS65910=y +CONFIG_GPIO_TWL4030=y +CONFIG_GPIO_MXC=y +CONFIG_POWER_AVS=y +CONFIG_ROCKCHIP_IODOMAIN=y +CONFIG_POWER_RESET_AS3722=y +CONFIG_POWER_RESET_GPIO=y +CONFIG_POWER_RESET_GPIO_RESTART=y +CONFIG_POWER_RESET_ST=y +CONFIG_POWER_RESET_KEYSTONE=y +CONFIG_POWER_RESET_RMOBILE=y +CONFIG_BATTERY_ACT8945A=y +CONFIG_BATTERY_CPCAP=m +CONFIG_BATTERY_SBS=y +CONFIG_BATTERY_BQ27XXX=m +CONFIG_AXP20X_POWER=m +CONFIG_BATTERY_MAX17040=m +CONFIG_BATTERY_MAX17042=m +CONFIG_CHARGER_CPCAP=m +CONFIG_CHARGER_GPIO=m +CONFIG_CHARGER_MAX14577=m +CONFIG_CHARGER_MAX77693=m +CONFIG_CHARGER_MAX8997=m +CONFIG_CHARGER_MAX8998=m +CONFIG_CHARGER_TPS65090=y +CONFIG_SENSORS_ASPEED=m +CONFIG_SENSORS_IIO_HWMON=y +CONFIG_SENSORS_LM90=y +CONFIG_SENSORS_LM95245=y +CONFIG_SENSORS_NTC_THERMISTOR=m +CONFIG_SENSORS_PWM_FAN=m +CONFIG_SENSORS_RASPBERRYPI_HWMON=m +CONFIG_SENSORS_INA2XX=m +CONFIG_CPU_THERMAL=y +CONFIG_DEVFREQ_THERMAL=y +CONFIG_IMX_THERMAL=y +CONFIG_ROCKCHIP_THERMAL=y +CONFIG_RCAR_THERMAL=y +CONFIG_ARMADA_THERMAL=y +CONFIG_BCM2711_THERMAL=m +CONFIG_BCM2835_THERMAL=m +CONFIG_BRCMSTB_THERMAL=m +CONFIG_ST_THERMAL_MEMMAP=y +CONFIG_UNIPHIER_THERMAL=y +CONFIG_DA9063_WATCHDOG=m +CONFIG_ARM_SP805_WATCHDOG=y +CONFIG_AT91SAM9X_WATCHDOG=y +CONFIG_SAMA5D4_WATCHDOG=y +CONFIG_S3C2410_WATCHDOG=m +CONFIG_DAVINCI_WATCHDOG=m +CONFIG_ORION_WATCHDOG=y +CONFIG_RN5T618_WATCHDOG=y +CONFIG_SUNXI_WATCHDOG=y +CONFIG_IMX2_WDT=y +CONFIG_ST_LPC_WATCHDOG=y +CONFIG_TEGRA_WATCHDOG=m +CONFIG_MESON_WATCHDOG=y +CONFIG_DIGICOLOR_WATCHDOG=y +CONFIG_RENESAS_WDT=m +CONFIG_RENESAS_RZAWDT=m +CONFIG_STPMIC1_WATCHDOG=y +CONFIG_BCM47XX_WDT=y +CONFIG_BCM2835_WDT=y +CONFIG_BCM_KONA_WDT=y +CONFIG_BCM7038_WDT=m +CONFIG_BCMA_HOST_SOC=y +CONFIG_BCMA_DRIVER_GMAC_CMN=y +CONFIG_BCMA_DRIVER_GPIO=y +CONFIG_MFD_ACT8945A=y +CONFIG_MFD_AC100=y +CONFIG_MFD_AXP20X_RSB=y +CONFIG_MFD_CPCAP=y +CONFIG_MFD_QCOM_RPM=y +CONFIG_MFD_SPMI_PMIC=y +CONFIG_MFD_PALMAS=y +CONFIG_MFD_TPS65090=y +CONFIG_MFD_TPS65217=y +CONFIG_MFD_TPS65218=y +CONFIG_MFD_TPS6586X=y +CONFIG_MFD_TPS65910=y +CONFIG_MFD_STM32_LPTIMER=m +CONFIG_MFD_STPMIC1=y +CONFIG_REGULATOR_ACT8945A=y +CONFIG_REGULATOR_ANATOP=y +CONFIG_REGULATOR_AB8500=y +CONFIG_REGULATOR_AS3711=y +CONFIG_REGULATOR_AS3722=y +CONFIG_REGULATOR_AXP20X=y +CONFIG_REGULATOR_BCM590XX=y +CONFIG_REGULATOR_CPCAP=y +CONFIG_REGULATOR_GPIO=y +CONFIG_REGULATOR_MAX14577=m +CONFIG_REGULATOR_MAX8907=y +CONFIG_REGULATOR_MAX8997=m +CONFIG_REGULATOR_MAX8998=m +CONFIG_REGULATOR_MAX77686=y +CONFIG_REGULATOR_MAX77693=m +CONFIG_REGULATOR_MAX77802=y +CONFIG_REGULATOR_PALMAS=y +CONFIG_REGULATOR_PBIAS=y +CONFIG_REGULATOR_QCOM_RPM=y +CONFIG_REGULATOR_QCOM_SMD_RPM=m +CONFIG_REGULATOR_RK808=y +CONFIG_REGULATOR_RN5T618=y +CONFIG_REGULATOR_S2MPA01=m +CONFIG_REGULATOR_S2MPS11=y +CONFIG_REGULATOR_S5M8767=y +CONFIG_REGULATOR_STM32_BOOSTER=m +CONFIG_REGULATOR_STM32_VREFBUF=m +CONFIG_REGULATOR_STM32_PWR=y +CONFIG_REGULATOR_STPMIC1=y +CONFIG_REGULATOR_TI_ABB=y +CONFIG_REGULATOR_TPS62360=y +CONFIG_REGULATOR_TPS65090=y +CONFIG_REGULATOR_TPS65217=y +CONFIG_REGULATOR_TPS65218=y +CONFIG_REGULATOR_TPS6586X=y +CONFIG_REGULATOR_TPS65910=y +CONFIG_REGULATOR_VEXPRESS=y +CONFIG_REGULATOR_WM8994=m +CONFIG_MEDIA_CEC_SUPPORT=y +CONFIG_VIDEO_MMP_CAMERA=m +CONFIG_VIDEO_ASPEED=m +CONFIG_VIDEO_STM32_DCMI=m +CONFIG_VIDEO_RENESAS_CEU=m +CONFIG_VIDEO_SAMSUNG_EXYNOS4_IS=m +CONFIG_VIDEO_S5P_FIMC=m +CONFIG_VIDEO_S5P_MIPI_CSIS=m +CONFIG_VIDEO_EXYNOS_FIMC_LITE=m +CONFIG_VIDEO_EXYNOS4_FIMC_IS=m +CONFIG_VIDEO_RCAR_VIN=m +CONFIG_VIDEO_ATMEL_ISI=m +CONFIG_VIDEO_SAMSUNG_S5P_JPEG=m +CONFIG_VIDEO_SAMSUNG_S5P_MFC=m +CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC=m +CONFIG_VIDEO_STI_BDISP=m +CONFIG_VIDEO_STI_HVA=m +CONFIG_VIDEO_STI_DELTA=m +CONFIG_VIDEO_RENESAS_FDP1=m +CONFIG_VIDEO_RENESAS_JPU=m +CONFIG_VIDEO_RENESAS_VSP1=m +CONFIG_V4L_TEST_DRIVERS=y +CONFIG_VIDEO_VIVID=m +CONFIG_CEC_PLATFORM_DRIVERS=y +CONFIG_CEC_SAMSUNG_S5P=m +CONFIG_VIDEO_ADV7180=m +CONFIG_VIDEO_ADV7604=m +CONFIG_VIDEO_ADV7604_CEC=y +CONFIG_VIDEO_ML86V7667=m +CONFIG_IMX_IPUV3_CORE=m +# CONFIG_DRM_I2C_CH7006 is not set +# CONFIG_DRM_I2C_SIL164 is not set +CONFIG_DRM_EXYNOS_FIMD=y +CONFIG_DRM_EXYNOS_MIXER=y +CONFIG_DRM_EXYNOS_DPI=y +CONFIG_DRM_EXYNOS_DSI=y +CONFIG_DRM_EXYNOS_HDMI=y +CONFIG_DRM_ROCKCHIP=m +CONFIG_ROCKCHIP_ANALOGIX_DP=y +CONFIG_ROCKCHIP_DW_HDMI=y +CONFIG_ROCKCHIP_DW_MIPI_DSI=y +CONFIG_ROCKCHIP_INNO_HDMI=y +CONFIG_DRM_IMX=m +CONFIG_DRM_IMX_PARALLEL_DISPLAY=m +CONFIG_DRM_IMX_TVE=m +CONFIG_DRM_IMX_LDB=m +CONFIG_DRM_IMX_HDMI=m +CONFIG_DRM_RCAR_DU=m +CONFIG_DRM_SUN4I=m +CONFIG_DRM_MSM=m +CONFIG_DRM_TEGRA=y +CONFIG_DRM_STM_DSI=m +CONFIG_DRM_SIMPLE_BRIDGE=m +CONFIG_DRM_I2C_ADV7511_AUDIO=y +CONFIG_DRM_VC4=m +CONFIG_DRM_ASPEED_GFX=m +CONFIG_FB_WM8505=y +CONFIG_FB_SH_MOBILE_LCDC=y +CONFIG_BACKLIGHT_AS3711=y +CONFIG_SND_HDA_TEGRA=m +CONFIG_SND_HDA_INPUT_BEEP=y +CONFIG_SND_HDA_PATCH_LOADER=y +CONFIG_SND_HDA_CODEC_REALTEK=m +CONFIG_SND_HDA_CODEC_HDMI=m +CONFIG_SND_ATMEL_SOC_WM8904=m +CONFIG_SND_ATMEL_SOC_PDMIC=m +CONFIG_SND_ATMEL_SOC_I2S=m +CONFIG_SND_BCM2835_SOC_I2S=m +CONFIG_SND_MMP_SOC=y +CONFIG_SND_PXA_SOC_SSP=m +CONFIG_SND_PXA910_SOC=m +CONFIG_SND_SOC_ROCKCHIP=m +CONFIG_SND_SOC_ROCKCHIP_SPDIF=m +CONFIG_SND_SOC_ROCKCHIP_MAX98090=m +CONFIG_SND_SOC_ROCKCHIP_RT5645=m +CONFIG_SND_SOC_SAMSUNG=m +CONFIG_SND_SOC_SAMSUNG_SMDK_WM8994=m +CONFIG_SND_SOC_SMDK_WM8994_PCM=m +CONFIG_SND_SOC_SNOW=m +CONFIG_SND_SOC_ODROID=m +CONFIG_SND_SOC_ARNDALE=m +CONFIG_SND_SOC_SH4_FSI=m +CONFIG_SND_SOC_RCAR=m +CONFIG_SND_SOC_STI=m +CONFIG_SND_SOC_STM32_SAI=m +CONFIG_SND_SOC_STM32_I2S=m +CONFIG_SND_SUN4I_CODEC=m +CONFIG_SND_SOC_TEGRA=m +CONFIG_SND_SOC_TEGRA20_I2S=m +CONFIG_SND_SOC_TEGRA30_I2S=m +CONFIG_SND_SOC_TEGRA_RT5640=m +CONFIG_SND_SOC_TEGRA_WM8753=m +CONFIG_SND_SOC_TEGRA_WM8903=m +CONFIG_SND_SOC_TEGRA_WM9712=m +CONFIG_SND_SOC_TEGRA_TRIMSLICE=m +CONFIG_SND_SOC_TEGRA_ALC5632=m +CONFIG_SND_SOC_TEGRA_MAX98090=m +CONFIG_SND_SOC_CS42L51_I2C=m +CONFIG_SND_SOC_SPDIF=m +CONFIG_USB_OTG=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_MVEBU=y +CONFIG_USB_XHCI_TEGRA=m +CONFIG_USB_EHCI_HCD_STI=y +CONFIG_USB_EHCI_TEGRA=y +CONFIG_USB_EHCI_EXYNOS=y +CONFIG_USB_EHCI_MV=m +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_STI=y +CONFIG_USB_OHCI_EXYNOS=m +CONFIG_USB_R8A66597_HCD=m +CONFIG_USB_RENESAS_USBHS=m +CONFIG_USB_STORAGE=y +CONFIG_USB_UAS=m +CONFIG_USB_MUSB_SUNXI=m +CONFIG_USB_MUSB_TUSB6010=m +CONFIG_USB_MUSB_UX500=m +CONFIG_USB_UX500_DMA=y +CONFIG_USB_INVENTRA_DMA=y +CONFIG_USB_TUSB_OMAP_DMA=y +CONFIG_USB_CHIPIDEA_UDC=y +CONFIG_USB_CHIPIDEA_HOST=y +CONFIG_USB_ISP1760=y +CONFIG_USB_HSIC_USB3503=y +CONFIG_AB8500_USB=y +CONFIG_KEYSTONE_USB_PHY=m +CONFIG_TWL6030_USB=m +CONFIG_USB_GPIO_VBUS=y +CONFIG_USB_ISP1301=y +CONFIG_USB_MXS_PHY=y +CONFIG_USB_FSL_USB2=y +CONFIG_USB_RENESAS_USBHS_UDC=m +CONFIG_USB_ASPEED_VHUB=m +CONFIG_USB_CONFIGFS_F_UAC1_LEGACY=y +CONFIG_MMC_BLOCK_MINORS=16 +CONFIG_MMC_ARMMMCI=y +CONFIG_MMC_SDHCI_OF_ARASAN=y +CONFIG_MMC_SDHCI_OF_AT91=y +CONFIG_MMC_SDHCI_OF_ESDHC=y +CONFIG_MMC_SDHCI_ESDHC_IMX=y +CONFIG_MMC_SDHCI_DOVE=y +CONFIG_MMC_SDHCI_TEGRA=y +CONFIG_MMC_SDHCI_S3C=y +CONFIG_MMC_SDHCI_PXAV3=y +CONFIG_MMC_SDHCI_PXAV2=m +CONFIG_MMC_SDHCI_SPEAR=y +CONFIG_MMC_SDHCI_S3C_DMA=y +CONFIG_MMC_SDHCI_BCM_KONA=y +CONFIG_MMC_MESON_MX_SDIO=y +CONFIG_MMC_SDHCI_ST=y +CONFIG_MMC_OMAP=y +CONFIG_MMC_ATMELMCI=y +CONFIG_MMC_SDHCI_MSM=y +CONFIG_MMC_MVSDIO=y +CONFIG_MMC_SDHI=y +CONFIG_MMC_UNIPHIER=y +CONFIG_MMC_DW_EXYNOS=y +CONFIG_MMC_DW_ROCKCHIP=y +CONFIG_MMC_SH_MMCIF=y +CONFIG_MMC_SUNXI=y +CONFIG_MMC_BCM2835=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS_FLASH=m +CONFIG_LEDS_CPCAP=m +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_PWM=y +CONFIG_LEDS_MAX77693=m +CONFIG_LEDS_MAX8997=m +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_ONESHOT=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_BACKLIGHT=y +CONFIG_LEDS_TRIGGER_CPU=y +CONFIG_LEDS_TRIGGER_GPIO=y +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y +CONFIG_LEDS_TRIGGER_TRANSIENT=y +CONFIG_LEDS_TRIGGER_CAMERA=y +CONFIG_EDAC_HIGHBANK_MC=y +CONFIG_EDAC_HIGHBANK_L2=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_AC100=y +CONFIG_RTC_DRV_AS3722=y +CONFIG_RTC_DRV_MAX8907=y +CONFIG_RTC_DRV_MAX8998=m +CONFIG_RTC_DRV_MAX8997=m +CONFIG_RTC_DRV_MAX77686=y +CONFIG_RTC_DRV_RK808=m +CONFIG_RTC_DRV_PCF85363=m +CONFIG_RTC_DRV_TWL4030=y +CONFIG_RTC_DRV_S5M=m +CONFIG_RTC_DRV_DA9063=m +CONFIG_RTC_DRV_EFI=m +CONFIG_RTC_DRV_DIGICOLOR=m +CONFIG_RTC_DRV_S3C=m +CONFIG_RTC_DRV_SA1100=m +CONFIG_RTC_DRV_SH=m +CONFIG_RTC_DRV_PL031=y +CONFIG_RTC_DRV_AT91RM9200=m +CONFIG_RTC_DRV_AT91SAM9=m +CONFIG_RTC_DRV_VT8500=y +CONFIG_RTC_DRV_SUNXI=y +CONFIG_RTC_DRV_MV=y +CONFIG_RTC_DRV_TEGRA=y +CONFIG_RTC_DRV_ST_LPC=y +CONFIG_RTC_DRV_STM32=y +CONFIG_RTC_DRV_CPCAP=m +CONFIG_RTC_DRV_ASPEED=m +CONFIG_DMADEVICES=y +CONFIG_AT_HDMAC=y +CONFIG_AT_XDMAC=y +CONFIG_DMA_BCM2835=y +CONFIG_DMA_SUN6I=y +CONFIG_FSL_EDMA=y +CONFIG_IMX_DMA=y +CONFIG_IMX_SDMA=y +CONFIG_MV_XOR=y +CONFIG_MXS_DMA=y +CONFIG_PL330_DMA=y +CONFIG_SIRF_DMA=y +CONFIG_STE_DMA40=y +CONFIG_ST_FDMA=m +CONFIG_STM32_DMA=y +CONFIG_STM32_DMAMUX=y +CONFIG_STM32_MDMA=y +CONFIG_TEGRA20_APB_DMA=y +CONFIG_UNIPHIER_MDMAC=y +CONFIG_XILINX_DMA=y +CONFIG_QCOM_BAM_DMA=y +CONFIG_DW_DMAC=y +CONFIG_RCAR_DMAC=y +CONFIG_RENESAS_USB_DMAC=m +CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO_MMIO=y +CONFIG_MFD_NVEC=y +CONFIG_KEYBOARD_NVEC=y +CONFIG_SERIO_NVEC_PS2=y +CONFIG_NVEC_POWER=y +CONFIG_NVEC_PAZ00=y +CONFIG_STAGING_BOARD=y +CONFIG_MFD_CROS_EC_DEV=m +CONFIG_CROS_EC_I2C=m +CONFIG_CROS_EC_SPI=m +CONFIG_COMMON_CLK_MAX77686=y +CONFIG_COMMON_CLK_RK808=m +CONFIG_COMMON_CLK_S2MPS11=m +CONFIG_CLK_RASPBERRYPI=y +CONFIG_COMMON_CLK_QCOM=y +CONFIG_QCOM_CLK_RPM=y +CONFIG_APQ_MMCC_8084=y +CONFIG_MSM_GCC_8660=y +CONFIG_MSM_MMCC_8960=y +CONFIG_MSM_MMCC_8974=y +CONFIG_BCM2835_MBOX=y +CONFIG_ROCKCHIP_IOMMU=y +CONFIG_TEGRA_IOMMU_GART=y +CONFIG_TEGRA_IOMMU_SMMU=y +CONFIG_EXYNOS_IOMMU=y +CONFIG_REMOTEPROC=y +CONFIG_ST_REMOTEPROC=m +CONFIG_ASPEED_LPC_CTRL=m +CONFIG_ASPEED_LPC_SNOOP=m +CONFIG_ASPEED_P2A_CTRL=m +CONFIG_RASPBERRYPI_POWER=y +CONFIG_QCOM_GSBI=y +CONFIG_QCOM_PM=y +CONFIG_QCOM_SMD_RPM=m +CONFIG_QCOM_WCNSS_CTRL=m +CONFIG_ARCH_R7S9210=y +CONFIG_ARCH_R8A7742=y +CONFIG_ARCH_R8A7743=y +CONFIG_ARCH_R8A7744=y +CONFIG_ARCH_R8A7745=y +CONFIG_ARCH_R8A77470=y +CONFIG_ARCH_R8A7792=y +CONFIG_ARCH_R9A06G032=y +CONFIG_ROCKCHIP_PM_DOMAINS=y +CONFIG_ARM_EXYNOS_BUS_DEVFREQ=m +CONFIG_ARM_TEGRA_DEVFREQ=m +CONFIG_DEVFREQ_EVENT_EXYNOS_NOCP=m +CONFIG_EXTCON_MAX14577=m +CONFIG_EXTCON_MAX77693=m +CONFIG_EXTCON_MAX8997=m +CONFIG_STM32_FMC2_EBI=y +CONFIG_EXYNOS5422_DMC=m +CONFIG_IIO=y +CONFIG_IIO_SW_TRIGGER=y +CONFIG_ASPEED_ADC=m +CONFIG_AT91_ADC=m +CONFIG_AT91_SAMA5D2_ADC=m +CONFIG_BERLIN2_ADC=m +CONFIG_CPCAP_ADC=m +CONFIG_EXYNOS_ADC=m +CONFIG_MESON_SARADC=m +CONFIG_ROCKCHIP_SARADC=m +CONFIG_STM32_ADC_CORE=m +CONFIG_STM32_ADC=m +CONFIG_STM32_DFSDM_ADC=m +CONFIG_VF610_ADC=m +CONFIG_XILINX_XADC=y +CONFIG_IIO_CROS_EC_SENSORS_CORE=m +CONFIG_IIO_CROS_EC_SENSORS=m +CONFIG_STM32_DAC=m +CONFIG_MPU3050_I2C=y +CONFIG_CM36651=m +CONFIG_IIO_CROS_EC_LIGHT_PROX=m +CONFIG_SENSORS_ISL29018=y +CONFIG_SENSORS_ISL29028=y +CONFIG_AK8975=y +CONFIG_IIO_HRTIMER_TRIGGER=y +CONFIG_IIO_STM32_LPTIMER_TRIGGER=m +CONFIG_PWM=y +CONFIG_PWM_ATMEL=m +CONFIG_PWM_ATMEL_HLCDC_PWM=m +CONFIG_PWM_ATMEL_TCB=m +CONFIG_PWM_BCM2835=y +CONFIG_PWM_BRCMSTB=m +CONFIG_PWM_FSL_FTM=m +CONFIG_PWM_MESON=m +CONFIG_PWM_RCAR=m +CONFIG_PWM_RENESAS_TPU=y +CONFIG_PWM_ROCKCHIP=m +CONFIG_PWM_SAMSUNG=m +CONFIG_PWM_STI=y +CONFIG_PWM_STM32=m +CONFIG_PWM_STM32_LP=m +CONFIG_PWM_SUN4I=y +CONFIG_PWM_TEGRA=y +CONFIG_PWM_VT8500=y +CONFIG_KEYSTONE_IRQ=y +CONFIG_PHY_SUN4I_USB=y +CONFIG_PHY_SUN9I_USB=y +CONFIG_PHY_HIX5HD2_SATA=y +CONFIG_PHY_BERLIN_SATA=y +CONFIG_PHY_BERLIN_USB=y +CONFIG_PHY_MMP3_USB=m +CONFIG_PHY_CPCAP_USB=m +CONFIG_PHY_QCOM_APQ8064_SATA=m +CONFIG_PHY_RCAR_GEN2=m +CONFIG_PHY_ROCKCHIP_DP=m +CONFIG_PHY_ROCKCHIP_USB=y +CONFIG_PHY_SAMSUNG_USB2=m +CONFIG_PHY_EXYNOS5250_SATA=m +CONFIG_PHY_UNIPHIER_USB2=y +CONFIG_PHY_UNIPHIER_USB3=y +CONFIG_PHY_MIPHY28LP=y +CONFIG_PHY_STIH407_USB=y +CONFIG_PHY_STM32_USBPHYC=y +CONFIG_PHY_TEGRA_XUSB=y +CONFIG_PHY_DM816X_USB=m +CONFIG_TI_PIPE3=y +CONFIG_TWL4030_USB=m +CONFIG_NVMEM_IMX_OCOTP=y +CONFIG_ROCKCHIP_EFUSE=m +CONFIG_NVMEM_SUNXI_SID=y +CONFIG_NVMEM_VF610_OCOTP=y +CONFIG_MESON_MX_EFUSE=m +CONFIG_FSI=m +CONFIG_FSI_MASTER_GPIO=m +CONFIG_FSI_MASTER_HUB=m +CONFIG_FSI_MASTER_ASPEED=m +CONFIG_FSI_SCOM=m +CONFIG_FSI_SBEFIFO=m +CONFIG_FSI_OCC=m +CONFIG_EXT4_FS=y +CONFIG_AUTOFS4_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_NTFS_FS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_SQUASHFS=y +CONFIG_SQUASHFS_LZO=y +CONFIG_SQUASHFS_XZ=y +CONFIG_PSTORE=y +CONFIG_PSTORE_CONSOLE=y +CONFIG_PSTORE_PMSG=y +CONFIG_PSTORE_RAM=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_NFS_V4_1=y +CONFIG_NFS_V4_2=y +CONFIG_ROOT_NFS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_UTF8=y +CONFIG_CRYPTO_USER=m +CONFIG_CRYPTO_USER_API_HASH=m +CONFIG_CRYPTO_USER_API_SKCIPHER=m +CONFIG_CRYPTO_USER_API_RNG=m +CONFIG_CRYPTO_USER_API_AEAD=m +CONFIG_CRYPTO_DEV_SUN4I_SS=m +CONFIG_CRYPTO_DEV_FSL_CAAM=m +CONFIG_CRYPTO_DEV_MARVELL_CESA=m +CONFIG_CRYPTO_DEV_EXYNOS_RNG=m +CONFIG_CRYPTO_DEV_S5P=m +CONFIG_CRYPTO_DEV_ATMEL_AES=m +CONFIG_CRYPTO_DEV_ATMEL_TDES=m +CONFIG_CRYPTO_DEV_ATMEL_SHA=m +CONFIG_CRYPTO_DEV_ROCKCHIP=m +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_CROS_EC=m +CONFIG_CROS_EC_CHARDEV=m +# Add config flags here that only appear in the multi_v7_defconfig +CONFIG_ARCH_ALPINE=n +CONFIG_ARCH_ARTPEC=n +CONFIG_ARCH_ASPEED=n +CONFIG_ARCH_AT91=n +CONFIG_ARCH_BCM=n +CONFIG_ARCH_BCM_CYGNUS=n +CONFIG_ARCH_BCM_NSP=n +CONFIG_ARCH_BCM_5301X=n +CONFIG_ARCH_BCM_281XX=n +CONFIG_ARCH_BCM_21664=n +CONFIG_ARCH_BRCMSTB=n +CONFIG_ARCH_BERLIN=n +CONFIG_ARCH_DIGICOLOR=n +CONFIG_ARCH_HIGHBANK=n +CONFIG_ARCH_HISI=n +CONFIG_ARCH_HI3xxx=n +CONFIG_ARCH_HIP01=n +CONFIG_ARCH_HIP04=n +CONFIG_ARCH_HIX5HD2=n +CONFIG_ARCH_MESON=n +CONFIG_ARCH_MXC=n +CONFIG_ARCH_MEDIATEK=n +CONFIG_ARCH_MILBEAUT=n +CONFIG_ARCH_QCOM=n +CONFIG_ARCH_MMP=n +CONFIG_ARCH_MSM8X60=n +CONFIG_ARCH_MSM8960=n +CONFIG_ARCH_MSM8974=n +CONFIG_ARCH_ROCKCHIP=n +CONFIG_ARCH_SOCFPGA=n +CONFIG_ARCH_SPEAR13XX=n +CONFIG_ARCH_STI=n +CONFIG_ARCH_EXYNOS=n +CONFIG_ARCH_SHMOBILE_MULTI=n +CONFIG_ARCH_EMEV2=n +CONFIG_ARCH_R7S72100=n +CONFIG_ARCH_R8A73A4=n +CONFIG_ARCH_R8A7740=n +CONFIG_ARCH_R8A7778=n +CONFIG_ARCH_R8A7779=n +CONFIG_ARCH_R8A7790=n +CONFIG_ARCH_R8A7791=n +CONFIG_ARCH_R8A7793=n +CONFIG_ARCH_R8A7794=n +CONFIG_ARCH_RENESAS=n +CONFIG_ARCH_SH73A0=n +CONFIG_ARCH_SUNXI=n +CONFIG_ARCH_STM32=n +CONFIG_ARCH_SIRF=n +CONFIG_ARCH_TEGRA=n +CONFIG_ARCH_TEGRA_2x_SOC=n +CONFIG_ARCH_TEGRA_3x_SOC=n +CONFIG_ARCH_TEGRA_114_SOC=n +CONFIG_ARCH_TEGRA_124_SOC=n +CONFIG_ARCH_UNIPHIER=n +CONFIG_ARCH_U8500=n +CONFIG_ARCH_VEXPRESS=n +CONFIG_ARCH_VEXPRESS_TC2_PM=n +CONFIG_ARCH_WM8850=n +CONFIG_ARCH_ZYNQ=n +CONFIG_ARCH_VIRT=n +CONFIG_ARCH_MVEBU=n +CONFIG_PLAT_SPEAR=n +CONFIG_CHROME_PLATFORMS=n +CONFIG_ARCH_OMAP2=n +CONFIG_ARCH_OMAP3=n +CONFIG_ARCH_OMAP4=n +CONFIG_ARCH_KEYSTONE=n + +# Multifunction device drivers +CONFIG_MFD_AS3711=n +CONFIG_MFD_AS3722=n +CONFIG_MFD_ATMEL_FLEXCOM=n +CONFIG_MFD_ATMEL_HLCDC=n +CONFIG_MFD_BCM590XX=n +CONFIG_MFD_AXP20X_I2C=n +CONFIG_MFD_DA9063=n +CONFIG_MFD_MAX14577=n +CONFIG_MFD_MAX77686=n +CONFIG_MFD_MAX77693=n +CONFIG_MFD_MAX8907=n +CONFIG_MFD_MAX8997=n +CONFIG_MFD_MAX8998=n +CONFIG_MFD_PM8XXX=n +CONFIG_MFD_RK808=n +CONFIG_MFD_RN5T618=n +CONFIG_MFD_SEC_CORE=n +CONFIG_MFD_STMPE=n + +# Regulators +CONFIG_REGULATOR_ACT8865=n +CONFIG_REGULATOR_DA9210=n +CONFIG_REGULATOR_FAN53555=n +CONFIG_REGULATOR_LP872X=n +CONFIG_REGULATOR_MAX8952=n +CONFIG_REGULATOR_MAX8973=n +CONFIG_REGULATOR_PWM=n +CONFIG_REGULATOR_TPS51632=n +CONFIG_REGULATOR_TWL4030=n + +# RTC drivers +CONFIG_RTC_DRV_HYM8563=n +CONFIG_RTC_DRV_RS5C372=n +CONFIG_RTC_DRV_BQ32K=n +CONFIG_RTC_DRV_S35390A=n +CONFIG_RTC_DRV_RX8581=n +CONFIG_RTC_DRV_EM3027=n + +# Watchdog Device Drivers +CONFIG_XILINX_WATCHDOG=n +CONFIG_DW_WATCHDOG=n + +# GPIO +CONFIG_GPIO_DWAPB=n +CONFIG_GPIO_XILINX=n +CONFIG_GPIO_TPS6586X=n + +# Sound +CONFIG_SND_ARM=n +CONFIG_SND_SPI=n +CONFIG_SND_ATMEL_SOC=n +CONFIG_SND_SOC_FSL_SAI=n +CONFIG_SND_SOC_AK4642=n +CONFIG_SND_SOC_CPCAP=n +CONFIG_SND_SOC_SGTL5000=n +CONFIG_SND_SOC_STI_SAS=n +CONFIG_SND_SOC_WM8978=n + +# DRM +CONFIG_VGA_ARB=n +CONFIG_DRM_I2C_ADV7511=n +CONFIG_DRM_NOUVEAU=n +CONFIG_DRM_EXYNOS=n +CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0=n +CONFIG_DRM_NXP_PTN3460=n +CONFIG_DRM_PARADE_PS8622=n +CONFIG_DRM_STI=n +CONFIG_DRM_PANEL_SAMSUNG_LD9040=n +CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03=n +CONFIG_DRM_DUMB_VGA_DAC=n +CONFIG_DRM_HISI_KIRIN=n +CONFIG_DRM_RCAR_LVDS=n +CONFIG_DRM_FSL_DCU=n +CONFIG_DRM_SII9234=n +CONFIG_DRM_MXSFB=n +CONFIG_DRM_HISI_HIBMC=n +CONFIG_DRM_ATMEL_HLCDC=n +CONFIG_DRM_STM=n +CONFIG_DRM_PANEL_ORISETECH_OTM8009A=n +CONFIG_DRM_PANEL_RAYDIUM_RM68200=n +CONFIG_DRM_TOSHIBA_TC358764=n +CONFIG_DRM_ETNAVIV=n +CONFIG_DRM_PL111=n +CONFIG_DRM_LIMA=n +CONFIG_DRM_PANFROST=n + +# Video +CONFIG_LCD_CLASS_DEVICE=n +CONFIG_FB_EFI=n +CONFIG_FB_SIMPLE=n +CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=n +CONFIG_FB_ARMCLCD=n +CONFIG_XEN_FBDEV_FRONTEND=n +################################################## +# TI Baseport Config Options +################################################## + +# Kernel compression +CONFIG_KERNEL_GZIP=n +CONFIG_KERNEL_LZMA=y +CONFIG_KERNEL_LZ4=n +CONFIG_KERNEL_XZ=n +CONFIG_KERNEL_LZO=n + +# Enable process accounting +CONFIG_BSD_PROCESS_ACCT=y + +# Have some way to pick up kernel config later on +# Always useful to look at /proc/config.gz +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y + +# Add Bin2c +CONFIG_BUILD_BIN2C=y + +# Add base Cgroups functions +CONFIG_CPUSETS=y +CONFIG_MEMCG=y + +# Choose CONFIG_EMBEDDED +CONFIG_EMBEDDED=y + +# Enable all kernel symbols please +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y + +# Enable AEABI +CONFIG_AEABI=y + +# How do we want kernel Modules to work? +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SRCVERSION_ALL=y + +# POSIX Message queue +CONFIG_POSIX_MQUEUE=y + +# No Multi Cluster systems in TI yet.. +CONFIG_MCPM=n + +# Serial +CONFIG_SERIAL_8250_OMAP=y +CONFIG_SERIAL_8250_OMAP_TTYO_FIXUP=y +CONFIG_SERIAL_8250_NR_UARTS=10 +CONFIG_SERIAL_8250_RUNTIME_UARTS=10 +CONFIG_SERIAL_8250_DMA=y +CONFIG_SERIAL_OMAP=n +CONFIG_SERIAL_8250_DW=n +CONFIG_SERIAL_8250_EM=n +CONFIG_SERIAL_AMBA_PL011=n +CONFIG_SERIAL_XILINX_PS_UART=n +CONFIG_SERIAL_FSL_LPUART=n +CONFIG_SERIAL_CONEXANT_DIGICOLOR=n +CONFIG_SERIAL_ST_ASC=n + +CONFIG_JUMP_LABEL=y + +# Disable Extra debug options +CONFIG_FTRACE=n +CONFIG_DEBUG_PREEMPT=n +CONFIG_SLUB_DEBUG=n +CONFIG_DEBUG_BUGVERBOSE=n + +CONFIG_PREEMPT=y +CONFIG_DEBUG_FS=y + +# Enable System V IPC +CONFIG_SYSVIPC=y + +# Power management options +CONFIG_PM_DEBUG=y + +# Clock framework stuff we need +CONFIG_COMMON_CLK_PALMAS=y +CONFIG_OMAP_RESET_CLOCKS=y + +# CPU Idle +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_CPU_IDLE_GOV_MENU=y +CONFIG_DT_IDLE_STATES=y + +# ARM CPU Idle Drivers +CONFIG_ARM_CPUIDLE=y +CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED=n + +# CPU Frequency scaling +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y + +# CPUFreq Driver Options +CONFIG_CPUFREQ_DT=y +CONFIG_ARM_BIG_LITTLE_CPUFREQ=n +CONFIG_ARM_KIRKWOOD_CPUFREQ=n +CONFIG_ARM_OMAP2PLUS_CPUFREQ=n +CONFIG_ARM_TI_CPUFREQ=y +CONFIG_QORIQ_CPUFREQ=n + +# AMx3 Power Config Options +CONFIG_WKUP_M3_RPROC=m +CONFIG_SOC_TI=y +CONFIG_WKUP_M3_IPC=m +CONFIG_AMX3_PM=m +CONFIG_SRAM=y +CONFIG_TI_EMIF_SRAM=m + +# Enable Reset Controllers +CONFIG_RESET_TI_SYSCON=y + +# Thermal +CONFIG_THERMAL_WRITABLE_TRIPS=y +CONFIG_THERMAL_GOV_FAIR_SHARE=y +CONFIG_THERMAL_GOV_BANG_BANG=y +CONFIG_THERMAL_GOV_USER_SPACE=y +CONFIG_TI_THERMAL=y +CONFIG_OMAP5_THERMAL=y +CONFIG_DRA752_THERMAL=y + +# Sensors +CONFIG_SENSORS_TMP102=y +CONFIG_SENSORS_GPIO_FAN=y + +# Enable the reset framework +CONFIG_POWER_RESET=y +CONFIG_POWER_SUPPLY=y + +# Pinctrl +CONFIG_PINCTRL_TI_IODELAY=y +CONFIG_PINCTRL_SINGLE=y + +# Multifunction device drivers +CONFIG_MFD_TPS65219=m +CONFIG_MFD_TPS6594X=y +CONFIG_TWL6040_CORE=y +CONFIG_MFD_TI_LP873X=y +CONFIG_MFD_TI_LP87565=y + +# Regulators +CONFIG_REGULATOR=y +CONFIG_REGULATOR_LP873X=y +CONFIG_REGULATOR_LP87565=y +CONFIG_REGULATOR_TPS65023=y +CONFIG_REGULATOR_TPS6507X=y +CONFIG_REGULATOR_TPS65219=m +CONFIG_REGULATOR_TPS6524X=y + +# Push Button +CONFIG_INPUT_TPS65219_PWRBUTTON=m + +# Crypto Modules +CONFIG_CRYPTO_DEV_OMAP=m +CONFIG_CRYPTO_DEV_OMAP_SHAM=m +CONFIG_CRYPTO_DEV_OMAP_AES=m +CONFIG_CRYPTO_DEV_OMAP_DES=m + +# RTC drivers +CONFIG_RTC_DRV_DS1307=m +CONFIG_RTC_DRV_PALMAS=m +CONFIG_RTC_DRV_TPS6586X=m +CONFIG_RTC_DRV_TPS65910=m +CONFIG_RTC_DRV_TPS6594X=y +CONFIG_RTC_DRV_TWL92330=y +CONFIG_RTC_DRV_OMAP=m + +# WatchDog +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +CONFIG_WATCHDOG_NOWAYOUT=n + +# Watchdog Device Drivers +CONFIG_OMAP_WATCHDOG=m +CONFIG_TWL4030_WATCHDOG=m + +# No Staging drivers please +CONFIG_STAGING=n + +# GPIO +CONFIG_GPIOLIB=y +CONFIG_GPIO_SYSFS=y +CONFIG_DEBUG_GPIO=y +CONFIG_GPIO_DAVINCI=y + +# Enable options to facilitate testing +CONFIG_CRYPTO_TEST=m +CONFIG_RTC_DEBUG=y +CONFIG_THERMAL_EMULATION=y + +# OPTEE Driver +CONFIG_TEE=y +CONFIG_OPTEE=y + +# DMA-BUF Heaps +CONFIG_DMABUF_HEAPS=y +CONFIG_DMABUF_HEAPS_SYSTEM=y +CONFIG_DMABUF_HEAPS_CMA=y +CONFIG_DMABUF_HEAPS_CARVEOUT=y +CONFIG_SRAM_DMA_HEAP=y + +# DMA-BUF exporter +CONFIG_DMA_BUF_PHYS=y +################################################## +# TI RPMsg/IPC Config Options +################################################## +# HwSpinLock +CONFIG_HWSPINLOCK=y +CONFIG_HWSPINLOCK_OMAP=y + +# Mailbox +CONFIG_MAILBOX=y +CONFIG_OMAP2PLUS_MBOX=y + +# SoC +CONFIG_TI_PRUSS=m + +# IrqChip Drivers +CONFIG_TI_PRUSS_INTC=m + +# IOMMU +CONFIG_IOMMU_SUPPORT=y +CONFIG_OMAP_IOMMU=y +CONFIG_OMAP_IOMMU_DEBUG=y + +# Remoteproc +CONFIG_OMAP_REMOTEPROC=m +CONFIG_OMAP_REMOTEPROC_WATCHDOG=y +CONFIG_PRU_REMOTEPROC=m +CONFIG_KEYSTONE_REMOTEPROC=m + +# RPMsg +CONFIG_RPMSG_CHAR=m +CONFIG_RPMSG_VIRTIO=m +CONFIG_RPMSG_PROTO=m +CONFIG_RPMSG_PRU=m + +# DSP Memory Mapper for Keystone MPM +CONFIG_KEYSTONE_DSP_MEM=m + +# UIO Module +CONFIG_UIO=m +################################################## +# TI Connectivity Configs +################################################## + +# Disable unused I2C options +CONFIG_I2C_MUX=n +CONFIG_I2C_DESIGNWARE_PLATFORM=n +CONFIG_I2C_GPIO=n +CONFIG_I2C_RK3X=n +CONFIG_I2C_XILINX=n + +# I2C controllers +CONFIG_I2C=y +CONFIG_I2C_OMAP=y + +# I2C GPIO expanders +CONFIG_GPIO_PCA953X=y +CONFIG_GPIO_PCA953X_IRQ=y +CONFIG_GPIO_PCF857X=y +CONFIG_GPIO_TPIC2810=m + +#I2C EEPROMS +CONFIG_EEPROM_AT24=m + +#SPI EEPROMS +CONFIG_EEPROM_93XX46=m + +# PTP +CONFIG_PTP_1588_CLOCK=y + +#Networking drivers +CONFIG_NET_VENDOR_TI=y +CONFIG_KEYSTONE_NAVIGATOR_QMSS=y +CONFIG_KEYSTONE_NAVIGATOR_DMA=y +CONFIG_TI_KEYSTONE_NETCP=y +CONFIG_TI_KEYSTONE_NETCP_ETHSS=y +CONFIG_TI_DAVINCI_EMAC=y +CONFIG_TI_DAVINCI_MDIO=y +CONFIG_MDIO_BITBANG=y +CONFIG_MDIO_GPIO=y +CONFIG_TI_CPSW=y +CONFIG_TI_CPSW_SWITCHDEV=y +CONFIG_TI_CPTS=y +CONFIG_TI_RDEV_ETH_SWITCH_VIRT_EMAC=m +CONFIG_TI_PRUETH=m +CONFIG_TI_ICSSG_PRUETH=m +CONFIG_TI_K3_AM65_CPSW_SWITCHDEV=y +# non-TI Net vendors +CONFIG_NET_DSA_BCM_SF2=n +CONFIG_B53=n +CONFIG_SYSTEMPORT=n +CONFIG_NET_VENDOR_3COM=n +CONFIG_NET_VENDOR_ADAPTEC=n +CONFIG_NET_VENDOR_AGERE=n +CONFIG_NET_VENDOR_ALTEON=n +CONFIG_NET_VENDOR_AMAZON=n +CONFIG_NET_VENDOR_AMD=n +CONFIG_NET_VENDOR_ARC=n +CONFIG_NET_VENDOR_ATHEROS=n +CONFIG_NET_VENDOR_BROCADE=n +CONFIG_NET_VENDOR_CAVIUM=n +CONFIG_NET_VENDOR_CHELSIO=n +CONFIG_BCMGENET=n +CONFIG_NET_VENDOR_CIRRUS=n +CONFIG_NET_VENDOR_CISCO=n +CONFIG_NET_VENDOR_DEC=n +CONFIG_NET_VENDOR_DLINK=n +CONFIG_NET_VENDOR_EMULEX=n +CONFIG_NET_VENDOR_EZCHIP=n +CONFIG_NET_VENDOR_FARADAY=n +CONFIG_NET_VENDOR_HISILICON=n +CONFIG_NET_VENDOR_HP=n +CONFIG_IGB=n +CONFIG_NET_VENDOR_I825XX=n +CONFIG_NET_VENDOR_MELLANOX=n +CONFIG_NET_VENDOR_MICROCHIP=n +CONFIG_NET_VENDOR_MYRI=n +CONFIG_NET_VENDOR_NATSEMI=n +CONFIG_NET_VENDOR_NETRONOME=n +CONFIG_NET_VENDOR_NVIDIA=n +CONFIG_NET_VENDOR_OKI=n +CONFIG_NET_VENDOR_8390=n +CONFIG_NET_VENDOR_QLOGIC=n +CONFIG_NET_VENDOR_REALTEK=n +CONFIG_NET_VENDOR_RENESAS=n +CONFIG_NET_VENDOR_RDC=n +CONFIG_NET_VENDOR_ROCKER=n +CONFIG_NET_VENDOR_SAMSUNG=n +CONFIG_NET_VENDOR_SILAN=n +CONFIG_NET_VENDOR_SIS=n +CONFIG_NET_VENDOR_SEEQ=n +CONFIG_NET_VENDOR_STMICRO=n +CONFIG_NET_VENDOR_SUN=n +CONFIG_NET_VENDOR_SYNOPSYS=n +CONFIG_NET_VENDOR_TEHUTI=n +CONFIG_NET_VENDOR_VIA=n +CONFIG_NET_VENDOR_WIZNET=n +#Wireless LAN +CONFIG_WLCORE=m +CONFIG_WLCORE_SDIO=m +CONFIG_WL18XX=m +CONFIG_NL80211_TESTMODE=y +CONFIG_MAC80211_MESH=y + +#MDIO phys +CONFIG_MARVELL_PHY=y +CONFIG_MICREL_PHY=y +# unused PHY drivers +CONFIG_BROADCOM_PHY=n +CONFIG_ICPLUS_PHY=n +#PRU MII PHYs for Industrial Boards +CONFIG_DP83848_PHY=y +# Enable phy for DRA72 evm +CONFIG_DP83867_PHY=y +# Enable phy for AM64 evm +CONFIG_DP83869_PHY=y +# Control onboard MDIO muxes +CONFIG_MDIO_BUS_MUX_MULTIPLEXER=y + +#MTD +CONFIG_MTD=y +CONFIG_MEMORY=y +CONFIG_TI_AEMIF=y +CONFIG_OMAP_GPMC=y +CONFIG_MTD_NAND_OMAP2=y +CONFIG_MTD_NAND_OMAP_BCH=y +CONFIG_MTD_NAND_OMAP_BCH_BUILD=y +CONFIG_MTD_NAND_DAVINCI=y +CONFIG_MTD_SPI_NAND=y +CONFIG_MTD_TESTS=m +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_UBI=y +CONFIG_MTD_HYPERBUS=y +CONFIG_HBMC_AM654=y + +#PCIE +CONFIG_SOCIONEXT_SYNQUACER_PREITS=y +CONFIG_PCI=y +CONFIG_PCI_MSI=y +CONFIG_PCI_ENDPOINT=y +CONFIG_PCI_ENDPOINT_CONFIGFS=y +CONFIG_PCI_EPF_TEST=y +CONFIG_PCI_EPF_NTB=y +CONFIG_PCI_ENDPOINT_TEST=m +CONFIG_PCI_DRA7XX=y +CONFIG_PCI_DRA7XX_HOST=y +CONFIG_PCI_DRA7XX_EP=y +CONFIG_PCI_KEYSTONE=y +CONFIG_PCI_KEYSTONE_HOST=y +CONFIG_PCI_KEYSTONE_EP=y +CONFIG_PCI_J721E=y +CONFIG_PCI_J721E_HOST=y +CONFIG_PCI_J721E_EP=y +CONFIG_PCIE_CADENCE=y +CONFIG_PCIE_CADENCE_HOST=y +CONFIG_PCIE_CADENCE_EP=y + +#NTB +CONFIG_NTB=m +CONFIG_NTB_EPF=m +CONFIG_NTB_TRANSPORT=m +CONFIG_NTB_NETDEV=m + +#NVME +CONFIG_NVME_CORE=m +CONFIG_BLK_DEV_NVME=m + +#These drivers have been used with DRA7x/AM57x PCIe RC with some success +CONFIG_NET_VENDOR_BROADCOM=y +CONFIG_NET_VENDOR_MARVELL=y +CONFIG_NET_VENDOR_INTEL=y +CONFIG_TIGON3=m +CONFIG_SKGE=m +CONFIG_E1000=m +CONFIG_E1000E=m +CONFIG_IWLWIFI=m +CONFIG_IWLDVM=m +CONFIG_IWLMVM=m +CONFIG_B43=m +#Generic Phys +CONFIG_PHY_AM654_SERDES=y +CONFIG_PHY_TI_KEYSTONE_SERDES=y +CONFIG_PHY_CADENCE_SIERRA=y + +# Networking +CONFIG_HSR=m +CONFIG_NF_CONNTRACK=m +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_ARPTABLES=m +CONFIG_IP_NF_ARPFILTER=m +CONFIG_IP_NF_ARP_MANGLE=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_BRIDGE=m +CONFIG_XFRM_USER=m +CONFIG_NET_KEY=m +CONFIG_INET=y +CONFIG_INET_AH=m +CONFIG_INET6_AH=m +CONFIG_INET6_IPCOMP=m +CONFIG_INET6_XFRM_TUNNEL=m +CONFIG_INET6_TUNNEL=m +CONFIG_INET_ESP=m +CONFIG_INET_IPCOMP=m +CONFIG_IPV6_TUNNEL=m +CONFIG_NETFILTER=y +CONFIG_NETFILTER_XTABLES=m +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m +CONFIG_NETFILTER_XT_TARGET_MARK=m +CONFIG_NETFILTER_XT_MATCH_COMMENT=m +CONFIG_NETFILTER_XT_MATCH_CPU=m +CONFIG_NETFILTER_XT_MATCH_IPRANGE=m +CONFIG_NETFILTER_XT_MATCH_LENGTH=m +CONFIG_NETFILTER_XT_MATCH_LIMIT=m +CONFIG_NETFILTER_XT_MATCH_MAC=m +CONFIG_NETFILTER_XT_MATCH_MARK=m +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m +CONFIG_NETFILTER_XT_MATCH_POLICY=m +CONFIG_NETFILTER_XT_MATCH_SCTP=m +CONFIG_BRIDGE_NF_EBTABLES=m +CONFIG_BRIDGE_EBT_BROUTE=m +CONFIG_BRIDGE_EBT_T_FILTER=m +CONFIG_BRIDGE_EBT_T_NAT=m +CONFIG_BRIDGE_EBT_802_3=m +CONFIG_BRIDGE_EBT_AMONG=m +CONFIG_BRIDGE_EBT_ARP=m +CONFIG_BRIDGE_EBT_IP=m +CONFIG_BRIDGE_EBT_IP6=m +CONFIG_BRIDGE_EBT_LIMIT=m +CONFIG_BRIDGE_EBT_MARK=m +CONFIG_BRIDGE_EBT_PKTTYPE=m +CONFIG_BRIDGE_EBT_STP=m +CONFIG_BRIDGE_EBT_VLAN=m +CONFIG_BRIDGE_EBT_ARPREPLY=m +CONFIG_BRIDGE_EBT_DNAT=m +CONFIG_BRIDGE_EBT_MARK_T=m +CONFIG_BRIDGE_EBT_REDIRECT=m +CONFIG_BRIDGE_EBT_SNAT=m +CONFIG_BRIDGE_EBT_LOG=m +CONFIG_BRIDGE_EBT_NFLOG=m +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CBQ=m +CONFIG_NET_SCH_HTB=m +CONFIG_NET_SCH_HFSC=m +CONFIG_NET_SCH_PRIO=m +CONFIG_NET_SCH_MULTIQ=m +CONFIG_NET_SCH_RED=m +CONFIG_NET_SCH_SFB=m +CONFIG_NET_SCH_SFQ=m +CONFIG_NET_SCH_TEQL=m +CONFIG_NET_SCH_TBF=m +CONFIG_NET_SCH_GRED=m +CONFIG_NET_SCH_DSMARK=m +CONFIG_NET_SCH_NETEM=m +CONFIG_NET_SCH_DRR=m +CONFIG_NET_SCH_MQPRIO=m +CONFIG_NET_SCH_TAPRIO=m +CONFIG_NET_SCH_CHOKE=m +CONFIG_NET_SCH_QFQ=m +CONFIG_NET_SCH_CODEL=m +CONFIG_NET_SCH_FQ_CODEL=m +CONFIG_NET_SCH_INGRESS=m +CONFIG_NET_CLS=y +CONFIG_NET_CLS_BASIC=m +CONFIG_NET_CLS_TCINDEX=m +CONFIG_NET_CLS_ROUTE4=m +CONFIG_NET_CLS_FW=m +CONFIG_NET_CLS_U32=m +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_RSVP=m +CONFIG_NET_CLS_RSVP6=m +CONFIG_NET_CLS_FLOW=m +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +CONFIG_NET_EMATCH_CMP=m +CONFIG_NET_EMATCH_NBYTE=m +CONFIG_NET_EMATCH_U32=m +CONFIG_NET_EMATCH_META=m +CONFIG_NET_EMATCH_TEXT=m +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=m +CONFIG_NET_ACT_GACT=m +CONFIG_GACT_PROB=y +CONFIG_NET_ACT_MIRRED=m +CONFIG_NET_ACT_IPT=m +CONFIG_NET_ACT_NAT=m +CONFIG_NET_ACT_PEDIT=m +CONFIG_NET_ACT_SIMP=m +CONFIG_NET_ACT_SKBEDIT=m +CONFIG_NET_ACT_CSUM=m +CONFIG_NET_SCH_FIFO=y +CONFIG_IP_SCTP=m +CONFIG_VLAN_8021Q=m +CONFIG_IP_MULTICAST=y +CONFIG_NET_SWITCHDEV=y + +# Ethernet drivers +CONFIG_TI_K3_AM65_CPSW_NUSS=y +CONFIG_TI_AM65_CPSW_TAS=y +CONFIG_TI_K3_AM65_CPTS=y +#MMC/SD support +CONFIG_MMC=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_OMAP_HS=y +CONFIG_MMC_SDHCI_OMAP=y +CONFIG_MMC_DW=n +CONFIG_MMC_SDHCI_AM654=y + +#SPI +CONFIG_SPI_CADENCE_QUADSPI=y +CONFIG_SPI_TI_QSPI=y +CONFIG_SPI_OMAP24XX=y +CONFIG_SPI_SLAVE=y +CONFIG_SPI_SPIDEV=m +#Disable unused SPI controllers +CONFIG_SPI_BITBANG=n +CONFIG_SPI_CADENCE=n +CONFIG_SPI_ROCKCHIP=n +CONFIG_SPI_XILINX=n + +#Disable SPI NOR 4K SECTORS +CONFIG_MTD_SPI_NOR_USE_4K_SECTORS=n + +#SPI GPIO expanders +CONFIG_GPIO_PISOSR=m + +#SATA +CONFIG_SATA_AHCI_PLATFORM=m +CONFIG_SATA_AHCI=m +CONFIG_ATA=m + +#USB PHY +CONFIG_OMAP_USB2=m + +#USB gadgets +CONFIG_USB_GADGET=m +CONFIG_USB_AUDIO=m +CONFIG_USB_ETH=m +CONFIG_USB_G_NCM=m +CONFIG_USB_GADGETFS=m +CONFIG_USB_FUNCTIONFS=m +CONFIG_USB_FUNCTIONFS_ETH=y +CONFIG_USB_FUNCTIONFS_RNDIS=y +CONFIG_USB_FUNCTIONFS_GENERIC=y +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_G_SERIAL=m +CONFIG_USB_MIDI_GADGET=m +CONFIG_USB_G_PRINTER=m +CONFIG_USB_CDC_COMPOSITE=m +CONFIG_USB_G_ACM_MS=m +CONFIG_USB_G_MULTI=m +CONFIG_USB_G_MULTI_CDC=y +CONFIG_USB_G_HID=m +CONFIG_USB_G_DBGP=m +CONFIG_USB_G_WEBCAM=m +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=32 +CONFIG_USB_ZERO=m +CONFIG_USB_CONFIGFS=m +CONFIG_USB_CONFIGFS_SERIAL=y +CONFIG_USB_CONFIGFS_ACM=y +CONFIG_USB_CONFIGFS_OBEX=y +CONFIG_USB_CONFIGFS_NCM=y +CONFIG_USB_CONFIGFS_ECM=y +CONFIG_USB_CONFIGFS_ECM_SUBSET=y +CONFIG_USB_CONFIGFS_RNDIS=y +CONFIG_USB_CONFIGFS_EEM=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +CONFIG_USB_CONFIGFS_F_LB_SS=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_UAC1=y +CONFIG_USB_CONFIGFS_F_UAC2=y +CONFIG_USB_CONFIGFS_F_MIDI=y +CONFIG_USB_CONFIGFS_F_HID=y +CONFIG_USB_CONFIGFS_F_UVC=y +CONFIG_USB_CONFIGFS_F_PRINTER=y + +# USB DWC3 +CONFIG_USB_DWC3=m +CONFIG_USB_DWC3_DUAL_ROLE=y +CONFIG_USB_DWC3_OMAP=m +CONFIG_USB_DWC3_KEYSTONE=m +CONFIG_USB_DWC3_AM62=m +CONFIG_USB_DWC3_PCI=n +CONFIG_USB_DWC2=n +CONFIG_USB_CHIPIDEA=n + +# USB CDNS3 +CONFIG_USB_CDNS3=m +CONFIG_USB_CDNS3_GADGET=y +CONFIG_USB_CDNS3_HOST=y +CONFIG_USB_CDNS3_TI=m + +#USB PHY +CONFIG_NOP_USB_XCEIV=m + +#USB MUSB +CONFIG_USB_MUSB_HDRC=m +CONFIG_USB_MUSB_DUAL_ROLE=y +CONFIG_USB_MUSB_OMAP2PLUS=m +CONFIG_USB_MUSB_AM35X=m +CONFIG_USB_MUSB_DSPS=m +CONFIG_USB_MUSB_AM335X_CHILD=m +CONFIG_TI_CPPI41=y +CONFIG_USB_TI_CPPI41_DMA=y +CONFIG_AM335X_CONTROL_USB=y +CONFIG_AM335X_PHY_USB=y + +#USB EHCI +CONFIG_USB=m +CONFIG_USB_EHCI_HCD=m +CONFIG_USB_EHCI_PCI=m +CONFIG_USB_EHCI_HCD_OMAP=m + +#USB Networking +CONFIG_USB_NET_DRIVERS=m +CONFIG_USB_PEGASUS=m +CONFIG_USB_RTL8150=m +CONFIG_USB_RTL8152=m +CONFIG_USB_LAN78XX=m +CONFIG_USB_USBNET=m +CONFIG_USB_NET_AX8817X=m +CONFIG_USB_NET_AX88179_178A=m +CONFIG_USB_NET_CDCETHER=m +CONFIG_USB_NET_CDC_EEM=m +CONFIG_USB_NET_CDC_NCM=m +CONFIG_USB_NET_SMSC75XX=m +CONFIG_USB_NET_SMSC95XX=m +CONFIG_USB_NET_NET1080=m +CONFIG_USB_NET_CDC_SUBSET_ENABLE=m +CONFIG_USB_NET_CDC_SUBSET=m +CONFIG_USB_BELKIN=y +CONFIG_USB_ARMLINUX=y +CONFIG_USB_NET_ZAURUS=m + +#USB testing +CONFIG_USB_TEST=m +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +#USB Serial +CONFIG_USB_ACM=m +CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL_CP210X=m +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_PL2303=m +CONFIG_USB_SERIAL_OPTION=m + +# Extcon drivers +CONFIG_EXTCON=m +CONFIG_EXTCON_PALMAS=m +CONFIG_EXTCON_USB_GPIO=m + +# USB PD controller drivers +CONFIG_TYPEC_TPS6598X=m + +# PWM +CONFIG_PWM_TIECAP=y +CONFIG_PWM_TIEHRPWM=y +CONFIG_PWM_TIPWMSS=y +CONFIG_PWM_OMAP_DMTIMER=y + +# eCAP driver +CONFIG_TI_ECAP_CAPTURE=m + +# 1-wire Bus Masters +CONFIG_W1=m +CONFIG_HDQ_MASTER_OMAP=m + +# Matrix keypad +CONFIG_KEYBOARD_MATRIX=m + +#Touchscreen/ADC +CONFIG_MFD_TI_AM335X_TSCADC=m +CONFIG_TOUCHSCREEN_TI_AM335X_TSC=m +CONFIG_TI_AM335X_ADC=m + +#CAN +CONFIG_CAN=m +CONFIG_CAN_C_CAN=m +CONFIG_CAN_C_CAN_PLATFORM=m +CONFIG_CAN_M_CAN=m +CONFIG_CAN_M_CAN_PLATFORM=m + +# CAN Transceiver +CONFIG_PHY_CAN_TRANSCEIVER=m + +# Rotary Encoder +CONFIG_INPUT_GPIO_DECODER=m + +# Filesystem extra options +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_CIFS=m +CONFIG_CIFS_STATS=y +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +CONFIG_UBIFS_FS=y + +# HD-Audio +CONFIG_SND_USB=y +CONFIG_SND_USB_AUDIO=m + +#UFS +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_SCSI_UFSHCD=y +CONFIG_SCSI_UFS_BSG=y +CONFIG_SCSI_UFSHCD_PLATFORM=y +CONFIG_SCSI_UFS_CDNS_PLATFORM=y +CONFIG_SCSI_UFS_TI_J721E=y + +#PRUSS-UART +CONFIG_SERIAL_8250_PRUSS=m + +# MUX Drivers +CONFIG_MUX_GPIO=y + +# PRU Soft UART driver +CONFIG_SERIAL_PRU_SWUART=m + +# Enable Power line communication Common drivers +CONFIG_NET_VENDOR_QUALCOMM=y +CONFIG_QCA7000=m +CONFIG_QCA7000_SPI=m +CONFIG_QCA7000_UART=m + +# AF_XDP +CONFIG_BPF_SYSCALL=y +CONFIG_XDP_SOCKETS=y +################################################## +# TI Audio/Display config options +################################################## + +CONFIG_CMA=y +CONFIG_DMA_CMA=y +CONFIG_CMA_SIZE_MBYTES=512 + +# backlight + +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_TLC591XX=y + +CONFIG_BACKLIGHT_PWM=y +CONFIG_BACKLIGHT_GPIO=y +CONFIG_BACKLIGHT_LED=y + +# fbdev + +CONFIG_FB_SSD1307=y +CONFIG_FRAMEBUFFER_CONSOLE=y + +# drm + +CONFIG_DRM=y +CONFIG_DRM_DISPLAY_CONNECTOR=y +CONFIG_DRM_SII902X=y +CONFIG_DRM_TI_TFP410=y +CONFIG_DRM_TI_TPD12S015=y +CONFIG_DRM_LVDS_CODEC=y +CONFIG_DRM_TOSHIBA_TC358767=y +CONFIG_DRM_TOSHIBA_TC358768=y +CONFIG_DRM_PANEL_SIMPLE=y +CONFIG_DRM_PANEL_OSD_OSD101T2587_53TS=y +# Firmware loading only works if built as module +CONFIG_DRM_CDNS_MHDP8546=m +CONFIG_DRM_CDNS_DSI=m + +CONFIG_PHY_J721E_WIZ=y +CONFIG_PHY_CADENCE_TORRENT=y + +# SGX driver needs legacy support +CONFIG_DRM_LEGACY=y +CONFIG_DRM_VM=y + +# tilcdc + +CONFIG_DRM_I2C_NXP_TDA998X=y +CONFIG_DRM_TILCDC=y + +# omapdrm + +CONFIG_DRM_OMAP=y +CONFIG_DRM_OMAP_WB=y +CONFIG_OMAP2_DSS=y +CONFIG_OMAP2_DSS_DEBUGFS=y +CONFIG_OMAP2_DSS_DPI=y +CONFIG_OMAP2_DSS_VENC=n +CONFIG_OMAP4_DSS_HDMI=y +CONFIG_OMAP5_DSS_HDMI=y +CONFIG_OMAP2_DSS_SDI=n +CONFIG_OMAP2_DSS_DSI=n + +# tidss +CONFIG_DRM_TIDSS=y +CONFIG_DRM_TIDSS_WB=y +CONFIG_DRM_TIDSS_DSS6=y +CONFIG_DRM_TIDSS_DSS7=y + +# Touchscreen + +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_EDT_FT5X06=m +CONFIG_TOUCHSCREEN_PIXCIR=m +CONFIG_TOUCHSCREEN_GOODIX=m +CONFIG_TOUCHSCREEN_ILI210X=m + +CONFIG_HID_MULTITOUCH=m + +# V4L2 + +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y + +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_VIDEO_AM437X_VPFE=m +CONFIG_VIDEO_TI_CAL=m +CONFIG_VIDEO_TI_VIP=m + +CONFIG_VIDEO_CADENCE=y +CONFIG_VIDEO_CADENCE_CSI2RX=m +CONFIG_PHY_CADENCE_DPHY=m +CONFIG_VIDEO_TI_J721E_CSI2RX=m + +CONFIG_V4L_MEM2MEM_DRIVERS=y +CONFIG_VIDEO_TI_VPE=m + +CONFIG_MEDIA_SUBDRV_AUTOSELECT=n +CONFIG_VIDEO_OV2659=m +CONFIG_VIDEO_OV1063X=m +CONFIG_VIDEO_MT9T11X=m +CONFIG_VIDEO_OV490=m +CONFIG_VIDEO_OV5640=m +CONFIG_VIDEO_IMX390=m + +CONFIG_VIDEO_DS90UB960=m +CONFIG_VIDEO_DS90UB953=m + +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_USB_VIDEO_CLASS=m +CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y + +CONFIG_MEDIA_ANALOG_TV_SUPPORT=n +CONFIG_MEDIA_DIGITAL_TV_SUPPORT=n +CONFIG_RC_CORE=n + +# sound +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_SIMPLE_CARD=m +CONFIG_SND_SIMPLE_SCU_CARD=m +CONFIG_SND_AUDIO_GRAPH_CARD=m +CONFIG_SND_AUDIO_GRAPH_SCU_CARD=m +CONFIG_SND_SOC_DAVINCI_MCASP=y +CONFIG_SND_SOC_OMAP_HDMI=m +CONFIG_SND_SOC_J721E_EVM=m +CONFIG_SND_SOC_TLV320AIC3X=m +CONFIG_SND_SOC_TLV320AIC31XX=m +CONFIG_SND_SOC_PCM3168A_I2C=m +CONFIG_SND_SOC_HDMI_CODEC=m + +# display sharing +CONFIG_RPMSG_KDRV_DISPLAY=y +################################################## +# TI Base OMAP generation SoCs generic default +################################################## + +CONFIG_ARCH_OMAP=y +CONFIG_SOC_OMAP5=y +CONFIG_SOC_AM33XX=y +CONFIG_SOC_AM43XX=y +CONFIG_ARCH_OMAP2PLUS=y +CONFIG_OMAP_INTERCONNECT_BARRIER=y +CONFIG_ARM_PSCI=n + +# Maximum Number of processors +CONFIG_NR_CPUS=2 + +# Kexec +CONFIG_KEXEC=y +################################################## +# TI DRA7 specific config options +################################################## + +# Supported ARM CPUs +CONFIG_ARCH_MULTI_V6=n +CONFIG_ARCH_MULTI_V7=y +CONFIG_ARCH_MULTI_V6_V7=n +CONFIG_CPU_V6=n + +# Enable CONFIG_SMP +CONFIG_SMP=y + +# Enable DRA7XX +CONFIG_SOC_DRA7XX=y + +# DRA7xx can support memory outside the 32bit address space +CONFIG_ARM_LPAE=y + +# EDAC +CONFIG_RAS=y +CONFIG_EDAC=y +CONFIG_EDAC_TI=m +############################################## +# +# Kernel options needed for systemd enabled TI SDKs +# See https://cgit.freedesktop.org/systemd/systemd/tree/README#n38 for details +# +############################################## +CONFIG_TMPFS=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_BLK_CGROUP=y +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_PERF=y + +CONFIG_INOTIFY_USER=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EPOLL=y +CONFIG_NET=y +CONFIG_SYSFS=y +CONFIG_PROC_FS=y +CONFIG_FHANDLE=y + +# udev will fail to work with the legacy sysfs layout: +CONFIG_SYSFS_DEPRECATED=n + +# Legacy hotplug slows down the system and confuses udev: +CONFIG_UEVENT_HELPER_PATH="" + +# Userspace firmware loading is not supported and should +# be disabled in the kernel: +CONFIG_FW_LOADER_USER_HELPER=n + +# Some udev rules and virtualization detection relies on it: +# Only for UEFI based systems +# http://cateee.net/lkddb/web-lkddb/DMI.html +CONFIG_DMIID=n + +# Support for some SCSI devices serial number retrieval, to +# create additional symlinks in /dev/disk/ and /dev/tape: +CONFIG_BLK_DEV_BSG=y + +# Required for PrivateNetwork and PrivateDevices in service units: +# Note that systemd-localed.service and other systemd units use +# PrivateNetwork and PrivateDevices so this is effectively required. +CONFIG_NAMESPACES=y +CONFIG_NET_NS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y + +# Optional but strongly recommended options: those are nice to have and +# indeed recommended, but not necessarily systemd required. These to be +# enabled in corresponding domain fragments since they are not specific +# to supporting systemd. + +CONFIG_SECCOMP=y +# for kcmp syscall +CONFIG_CHECKPOINT_RESTORE=y + +# Required for CPUShares= in resource control unit settings +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y + +# Required for CPUQuota= in resource control unit settings +CONFIG_CFS_BANDWIDTH=y + +# For systemd-bootchart, several proc debug interfaces are required: +# Systemd-debug.cfg? +CONFIG_SCHEDSTATS=y +CONFIG_SCHED_DEBUG=y + +# We recommend to turn off Real-Time group scheduling in the +# kernel when using systemd. RT group scheduling effectively +# makes RT scheduling unavailable for most userspace, since it +# requires explicit assignment of RT budgets to each unit whose +# processes making use of RT. As there's no sensible way to +# assign these budgets automatically this cannot really be +# fixed, and it's best to disable group scheduling hence. +CONFIG_RT_GROUP_SCHED=n + +# Note that kernel auditing is broken when used with systemd's +# container code. When using systemd in conjunction with +# containers, please make sure to either turn off auditing at +# runtime using the kernel command line option "audit=0", or +# turn it off at kernel compile time using: +CONFIG_AUDIT=n +############################################## +# +# Kernel options needed for container enabled TI SDKs +# See https://raw.githubusercontent.com/moby/moby/v20.10.12/contrib/check-config.sh +# Also see lxc-checkconfig +# +############################################## + +# Just the basic container configuration necessary +CONFIG_CGROUP_BPF=y +CONFIG_VETH=m +CONFIG_BRIDGE_NETFILTER=m +CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m +CONFIG_NETFILTER_XT_MATCH_IPVS=m +CONFIG_NET_CLS_CGROUP=m +CONFIG_IP_VS=m +CONFIG_IP_VS_RR=m +CONFIG_VXLAN=m +CONFIG_IPVLAN=m +CONFIG_MACVLAN=m +CONFIG_DUMMY=m +CONFIG_NF_NAT_FTP=m +CONFIG_NF_CONNTRACK_FTP=m +CONFIG_NF_NAT_TFTP=m +CONFIG_NF_CONNTRACK_TFTP=m +CONFIG_AUFS_FS=m +CONFIG_BTRFS_FS=m +CONFIG_BLK_DEV_DM=m +CONFIG_DM_THIN_PROVISIONING=m +CONFIG_OVERLAY_FS=m +CONFIG_INET_XFRM_MODE_TRANSPORT=m From 4b89f93dfc0389f138035df26cbd78174735933d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9?= Date: Wed, 15 Feb 2023 22:41:38 +0300 Subject: [PATCH 65/65] pm33xx.c: omap_rtc NULL dereferencing fixed On some board designs rtc is turned off so rtc node in devicetree has status = "disabled". In this case(according to am33xx_pm_rtc_setup) rtc-only mode is disabled and omap_rtc remains NULL. However, standby mode is still available. Implementation of am33xx_pm_end in vanilla kernel 5.2+ causes NULL dereferencing of omap_rtc for such boards. This patch fixes that. --- drivers/soc/ti/pm33xx.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/soc/ti/pm33xx.c b/drivers/soc/ti/pm33xx.c index 73be958f6aa5f..21c7a0968ca6d 100644 --- a/drivers/soc/ti/pm33xx.c +++ b/drivers/soc/ti/pm33xx.c @@ -304,10 +304,6 @@ static void am33xx_pm_end(void) u32 val = 0; struct nvmem_device *nvmem; - nvmem = devm_nvmem_device_get(&omap_rtc->dev, "omap_rtc_scratch0"); - if (IS_ERR(nvmem)) - return; - m3_ipc->ops->finish_low_power(m3_ipc); if (rtc_only_idle) { if (retrigger_irq) { @@ -322,9 +318,9 @@ static void am33xx_pm_end(void) gic_dist_base + GIC_INT_SET_PENDING_BASE + retrigger_irq / 32 * 4); } - - nvmem_device_write(nvmem, RTC_SCRATCH_MAGIC_REG * 4, 4, - (void *)&val); + nvmem = devm_nvmem_device_get(&omap_rtc->dev, "omap_rtc_scratch0"); + if (!IS_ERR(nvmem)) + nvmem_device_write(nvmem, RTC_SCRATCH_MAGIC_REG * 4, 4, (void *)&val); } rtc_only_idle = 0;